编译器给一个类默认生成的六个成员函数:
构造函数、析构函数、拷贝构造函数、赋值运算符重载、对象取地址运算符以及常对象取地址运算符。其中析构函数不能重载,默认的拷贝和赋值两个函数存在浅拷贝的问题。
对象取地址运算符重载和常对象取地址运算符重载如下:
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容。
#include<iostream>
using namespace std;
class Person
{
public:
Person* operator&()
{
return this;
}
const Person* operator&() const//这个const实际上是加到this指针前面了 this指针是 类型* const 指针常量
{
return this;
}
private:
char m_name[10];
int m_age;
};
1.引用和指针的区别
1. 引用在定义时必须初始化,指针没有要求
2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型 实体
3. 没有NULL引用,但有NULL指针
4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)
5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全
2.内联函数(加inline关键字),以空间换时间。声明为内联函数后编译器会在调用处对齐进行代码复制。直接将代码块在这里展开。减少了函数压栈(现场保护和现场恢复的时间)。
内联函数注意事项:1.内联只是一种建议,编译器会自己决定是否采纳。
2.内联函数不允许内部有循环和递归,它适用于调用次数多,代码简单的函数。
3.内联函数的的声明和定义一般都放在.h文件中,如果分开在头文件和源文件中会发生未定义的错误。引文内联函数的展开是在编译时完成的,链接会找不到。
#include<iostream>
using namespace std;
inline void add(int a, int b)
{
int c = a+b;
}
int main()
{
int a = 10,b=20;
add(a, b);
system("pause");
return 0;
}
#include<iostream>
#include"test.h"
using namespace std;
int main()
{
int a = 10,b=20;
add(a, b);
system("pause");
return 0;
}
#include<iostream>
using namespace std;
inline void add(int a, int b)
{
int c = a + b;
}
上面为.h文件。
2.auto自动类型推导
auto声明的变量必须由编译器在编译时期推导而得。使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类
型。
1. auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
2. 在同一行定义多个变量 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
注意事项:1.auto不能推导函数参数
2.auto不能推导数组
#include<iostream>
using namespace std;
void test01()
{
int a = 10;
auto* ptr = &a;
cout << typeid(ptr).name() << endl;
auto& ref = a;
cout << typeid(ref).name() << endl;
}
void test02()
{
int arr[5] = { 1,2,3,4,5 };
auto br = arr;
cout << typeid(br).name() << endl;//这里显示int *
br[3] = 5;
cout << arr[3] << endl;
}
void test03()
{
int arr[5] = { 1,2,3,4,5 };
auto& br = arr;//数组的引用
//int(&br)[5] = arr;//数组引用写法
cout << typeid(br).name() << endl;//这里会显示int [5]不是int *
br[3] = 5;
cout << arr[3] << endl;
}
int main()
{
test03();
system("pause");
return 0;
}
3.基于范围的for循环
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中 引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,
第二部分则表示被迭代的范围。
注意:1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的
方法,begin和end就是for循环迭代的范围。
#include<iostream>
using namespace std;
void test02(int array[])
{
for (auto e : array)//数组传参传的是地址,不存在开闭范围,不能这样循环
{
cout << e;
}
for (int i = 0; i < sizeof(array) / sizeof(int); i++)
cout << array[i];
cout << endl;
}
void test01()
{
int br[] = { 1,2,3,4,5 };
for (auto &e : br)
{
e *= 2;
cout << e;
}
cout << endl;
for (int i=0;i<sizeof(br)/sizeof(int);i++)
{
cout << br[i];
}
cout << endl;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
test01();
system("pause");
return 0;
}
3.空指针,NULL在底层被宏定义为0和void*(无类型指针),它可以作为void*使用也可以作为整形0使用,默认情况下会被当作0,所以NULL在函数重载等情况下会造成不便。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
#include<iostream>
using namespace std;
void func(int i)
{
cout << "func(int i)" << endl;
}
void func(int* p)
{
cout << "func(int* p)" << endl;
}
void test01()
{
cout << sizeof(nullptr) << endl;
cout<<sizeof((void*)NULL)<<endl;
}
int main()
{
func(NULL);//传0
func((int*)NULL);//传 int*
func(nullptr);//传空指针
test01();
system("pause");
return 0;
}