引用(reference)
-
引用是一个更安全的指针(a safe pointer), 借值之名,行指针之实,实际上表示的是地址。
-
引用必须初始化, 和C语言中野指针不同
int b = 0; int &a; //error int &a = b;
-
引用一旦定义,不可以再修改指向。与之不同,指针可以先后指向不同对象。
int &a = b; a = c; // assign the value of c to a
-
引用不可以完全取代指针,例如在
malloc
和new
中返回值只能是指针。
补充:内存分区问题
- 代码区:储存我们写的代码,只读常量,无法修改。
- 全局变量区(静态区):在程序运行开始,程序在内存中开辟一段区域,把这段区域全部置为0,用于存储全局变量。这也是就为什么全局变量不初始化值自动变为0。全局变量的初始化在main函数运行之前。
- 运行内存区(runtime memory):包括栈区和堆区
- 栈区:用于局部变量
- 栈区内存是连续的
- 堆区:用于动态内存分配(
malloc
和new
时)。
- 堆区内存是离散的,理论上来无限次使用
malloc
会导致程序崩溃- 应用场景:1.不能确定内存分配大小需要在运行时确定(例如链表 建立);2.需要人为控制变量的生命周期。
拷贝构造
C++的类默认支持用一个已存在的类初始化一个新建的类。
int main() {
struct Student* a = new Student();
struct Student b(*a);
}
C++中有拷贝有两种类型——
bitwise copy
(浅拷贝)logical copy
(深拷贝)
拷贝构造时默认是浅拷贝,即把对象a
所在内存空间中所有数据以字节的形式拷贝给新建的b对象。如果a对象中有指针成员,则只会把指针的值拷贝给b
,这样会出现a
,b
两个对象的指针成员指向同一块内存。(编译时不报错,非常恐怖!!!)
class Student {
private:
int a;
int *b;
public:
Student(int ai, int bi);
};
Student::Student(int ai, int bi) {
this->a = ai;
this->b = new int(bi);
}
int main() {
Student mem1(1, 2);
Student mem2(mem1);
cout << mem1.b << "\n" << mem2.b << endl; //两者指针值值一样的,即值指向同一块内存
}
为了避免这种情况,我们需要自定义一个拷贝构造函数从而实现 深拷贝,主要写法为Object::Object(const Object &a) {}
,具体例子如下所示
class Student {
private:
int a;
int *b;
public:
Student(int ai, int bi);
Student(const Student & s);
};
Student::Student(const Student & s) {
this->a = s.a;
this->b = new int(*(s.b));
}
int main() {
Student mem1(1, 2);
Student mem2(mem1);
cout << mem1.b << "\n" << mem2.b << endl; //这样两者指针值就是不一样的了
func(mem1);
return 0;
}
在C/C++中,“只要传值,就是一次拷贝构造”,当我们向一个函数传递实参,且这个函数的形参不是引用类型,此时程序会自动调用实参类型的拷贝构造函数。例如
class Student {
private:
int a;
int *b;
public:
Student(int ai, int bi);
Student(const Student & s);
};
Student::Student(const Student & s) {
cout << "调用自定义拷贝构造函数" << endl;
this->a = s.a;
this->b = new int(*(s.b));
}
void func(Student s) {
cout << "实参调用" << endl;
}
int main() {
func(mem1);
return 0;
}
/**
以上程序执行结果为——
调用自定义拷贝构造函数
实参调用
/
什么使用需要深拷贝???
- 类构造函数中有new(说明有指针成员),一定要深拷贝!
- 构造函数中没有new也不一定不需要深拷贝,有可能在其他位置出现new或者malloc
函数参数问题
- Pass by value ?
- Pass by address ?
传值和传地址在功能、性能和其他方面的比较如下——
Item | 传值 | 传地址 |
---|---|---|
功能 | 函数只能input,即只能使用实参的值,不能改变实参的值 | 函数既能input也能output,即不仅可以使用实参的值,也可以改变实参的值 |
性能 | 需要传递sizeof(value) 大小的数据 |
只需要传递sizeof(int) 大小的数据 |
其他 | 只要传值,就需要调用一次拷贝构造 | 无 |
结论:不要使用pass by value
!!!