一.引用
1.引用的定义
类型名& 引用变量名 = 变量名
1-1.举例
int a = 10;
int& b = a;
2.特性
引用就是给一个内存段取别名
引用不占内存
引用必须初始化
2-1.图解
定义整形变量a,a的首地址为0x00004567,首地址对应的内存段为红框部分,内存段中存放的数据为10
定义引用类型b,用a初始化b,相当于a,b共享一块内存段
生活中的例子:一个人名字叫刘备、字玄德,那么变量a就代表刘备,引用类型b就代表玄德,本质上这两种叫法都是叫的同一个人
同样,b只是a的另一个称呼,他们所代表的都是同一个内存段,改变b的值a的值会变,改变a的值b的值也会改变
引用必须初始化,假设没有人叫刘备,那么玄德也就不能代表一个具体的人,只有知道刘备、字玄德,才能确定玄德代表的就是刘备这个人
二.参数传递的三种方式
1.值传递
void show (int a);
把变量的值赋值给形参变量
2.地址传递
void show (int* a);
把变量的首地址赋值给形参指针
3.引用传递
void show (int& a);
把变量所代表的内存段与形参绑定
三.拷贝构造
1.拷贝构造函数的定义
类名 (const 类名& 变量名) { };
1-1.举例
A (const A& a) { };
2.特性
本质上拷贝构造就是构造函数
用对象来构造对象,就会调用拷贝构造函数
缺省构造函数使用的方式是浅拷贝
3.拷贝构造只能用引用类型
A (A a) {};
定义一个形参变量,调用时,实参赋值给形参,形参要接收这个值,要构造出A类对象,只能调用构造函数A(A a)来构造函数
但是在构造对象时,又要构造出一个形参变量来接收这个值,无限循环,永远也构造不出来一个对象。
A (A* a) {};
地址传递同理
4.验证拷贝构造的存在
class A { int a; public: A(); A(int b); } A:A() { printf("默认构造函数!\n"); } A:A(int b) { printf("构造函数重载!\n"); }
int main()
{
A a;
A b(1);
A c(a);
A d = b;
return 0;
}
由输出结果可以看出,除了构造函数之外,默认还会存在一种函数,这就是拷贝构造函数
class A { int a; public: A(); A(int b); A(const A& b); } A:A() { printf("默认构造函数!\n"); } A:A(int b) { printf("构造函数重载!\n"); } A:A(const A& b) { printf("拷贝构造函数!\n"); } int main() { A a; A b(1); A c(a); A d = b; return 0; }
5.浅拷贝
5-1.原理
直接复制,保存指针变量地址
5-2.举例
char* ch = new char[256];
ch = "Hello world!";
char* p = ch;
delete[] ch;
delete[] p;
定义指针变量ch,开辟256字节的内存段,赋值为"Hello World"。
定义指针变量p,p保存指针ch的地址,此时p和ch指向的是同一个内存段
若指针ch所保存的值改变,指针p的值也会改变
若释放指针ch指向的内存段,再释放p指向的内存段是就会出错
6.深拷贝
6-1.原理
重新分配内存,拷贝数据
6-2.举例
char* ch = new char[256];
ch = "Hello World!";
char* p = new char[256];
memcpy(p,ch,256);
delete[] ch;
delete[] p;
定义指针变量ch,开辟256字节的内存段,赋值为"Hello World"。
定义指针变量p,给指针p开辟256字节的内存段,把指针ch指向内存段的值,拷贝到指针p指向的新开内存段
由于指针p与指针ch指向的不同的内存段,因此就算指针ch的值改变,指针p的值也不会改变
此时释放指针ch所指向内存段,在释放指针p所指针的内存段,就不会出错