目录
1.拷贝构造函数是什么
2.拷贝构造函数的基本格式
2.1 默认拷贝构造函数(浅拷贝)
2.2 深拷贝(Deep Copy)
2.3 浅拷贝(Shallow Copy)
2.3 浅拷贝和深拷贝总结
2.友元函数
1.拷贝构造函数是什么
拷贝构造函数是一个特殊的构造函数,用于在创建新对象时,用已有对象的数据来初始化新对象。拷贝构造函数的典型应用场景包括:
- 传值参数:当一个对象通过值传递给函数时,编译器会调用拷贝构造函数来创建函数参数的副本。
- 为了将
obj1
传递给passByValue
,C++ 会调用 拷贝构造函数 创建一个新的MyClass
对象。这个新的对象会有自己的str
,内容是obj1.str
。 - 在
passByValue
函数内部,调用obj.display()
显示副本对象的数据。 - 注意:
obj
是一个副本,函数执行完后,它会被销毁,所做的任何更改都不会影响obj1
。
- 返回值:当函数返回一个对象时,编译器会用返回的对象来创建一个临时副本,然后将该副本返回给调用者。
- 在
returnByValue
函数中,创建了一个局部对象temp
,它的str
初始化为"临时对象"
。 - 然后函数通过
return temp;
将temp
返回给调用者。 - 返回时,C++ 会调用 拷贝构造函数 来创建一个新的对象,这个新对象的内容就是
temp
对象的副本。这里的拷贝构造函数会执行字符串的复制操作,确保新对象拥有自己的str
数据。
- 对象初始化:创建新对象并将另一个对象赋值给它时,例如
MyClass obj2 = obj1;
#include <iostream>
#include <cstring>class MyClass {char* str;public:// 普通构造函数MyClass(const char* s) {str = new char[strlen(s) + 1];strcpy(str, s);std::cout << "调用普通构造函数" << std::endl;}// 拷贝构造函数MyClass(const MyClass &obj) {str = new char[strlen(obj.str) + 1];strcpy(str, obj.str);std::cout << "调用拷贝构造函数" << std::endl;}// 显示字符串void display() const {std::cout << "字符串内容: " << str << std::endl;}// 析构函数~MyClass() {delete[] str;}
};// 示例 1:传值参数
void passByValue(MyClass obj) {obj.display();
}// 示例 2:返回值
MyClass returnByValue() {MyClass temp("临时对象");return temp;
}int main() {// 示例 3:对象初始化MyClass obj1("Hello"); // 调用普通构造函数MyClass obj2 = obj1; // 调用拷贝构造函数 (对象初始化)// 示例 1:传值参数passByValue(obj1); // 调用拷贝构造函数 (传值参数)// 示例 2:返回值MyClass obj3 = returnByValue(); // 调用拷贝构造函数 (返回值)obj1.display();obj2.display();obj3.display();return 0;
}
2.拷贝构造函数的基本格式
拷贝构造函数的格式如下:
ClassName(const ClassName &other);
这里的参数 other
是对同类型对象的常量引用,
使用引用的原因是避免在传参时调用拷贝构造函数,导致递归调用;而使用常量引用可以避免修改原对象。
如果other
是传值传递的,也就是说,编译器需要创建 other
的副本。而为了创建这个副本,编译器会再次调用拷贝构造函数——这样就会导致递归调用,拷贝构造函数会一遍一遍地自己调用自己,最终导致栈溢出错误
2.1 默认拷贝构造函数(浅拷贝)
如果没有定义拷贝构造函数,编译器会生成一个默认拷贝构造函数,对每个数据成员进行浅拷贝。浅拷贝的方式是逐个复制数据成员,即将源对象的每个成员变量直接赋值给新对象。
浅拷贝对包含指针的类可能不适用。如果类中包含动态分配的资源(例如 new
分配的内存),浅拷贝会导致新旧对象指向相同的内存,可能导致一些问题:
- 双重释放:如果两个对象指向同一块内存,当一个对象析构时会释放这块内存,而当另一个对象析构时会再次尝试释放这块内存,从而引发错误。
- 数据混乱:如果一个对象修改了内存的内容,那么另一个对象也会受影响,因为它们共享同一块内存。
为了避免这些问题,通常需要自定义拷贝构造函数。
2.2 深拷贝(Deep Copy)
深拷贝会创建一个新内存区域,并复制实际的数据到新内存,这样两个对象互不影响。深拷贝适用于包含指针成员的类。
#include <iostream>
#include <cstring>class DeepCopyClass {
public:char* data;// 构造函数DeepCopyClass(const char* str) {data = new char[strlen(str) + 1];strcpy(data, str);}// 深拷贝构造函数DeepCopyClass(const DeepCopyClass &obj) {data = new char[strlen(obj.data) + 1];strcpy(data, obj.data); // 复制实际内容}~DeepCopyClass() {delete[] data;}
};int main() {DeepCopyClass obj1("Hello");DeepCopyClass obj2 = obj1; // 调用深拷贝构造函数// 修改 obj2 的数据strcpy(obj2.data, "World");std::cout << "obj1 数据: " << obj1.data << std::endl; // 输出 "Hello",因为两者独立std::cout << "obj2 数据: " << obj2.data << std::endl;return 0;
}
2.3 浅拷贝(Shallow Copy)
浅拷贝仅复制对象的非动态内存内容或指针的地址。这意味着:
- 浅拷贝会将指针地址复制到新对象中,但不会创建新内存。新对象的指针与原对象指向相同的内存区域。
- 如果两个对象共享同一内存区域时,一个对象的修改会影响另一个对象。
- 当程序试图释放内存时,会因为多次释放相同的内存而导致悬挂指针或内存泄漏问题。
#include <iostream>
#include <cstring>class ShallowCopyClass {
public:char* data;// 构造函数ShallowCopyClass(const char* str) {data = new char[strlen(str) + 1];strcpy(data, str);}// 浅拷贝构造函数ShallowCopyClass(const ShallowCopyClass &obj) {data = obj.data; // 仅复制指针地址(浅拷贝)}~ShallowCopyClass() {delete[] data;}
};int main() {ShallowCopyClass obj1("Hello");ShallowCopyClass obj2 = obj1; // 调用浅拷贝构造函数// 修改 obj2 的数据strcpy(obj2.data, "World");std::cout << "obj1 数据: " << obj1.data << std::endl; // 输出 "World",因为两者共享相同内存std::cout << "obj2 数据: " << obj2.data << std::endl;return 0;
}
2.3 浅拷贝和深拷贝总结
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
内存分配 | 复制指针地址,指向同一内存 | 分配新内存,复制数据 |
内存管理 | 共用同一内存,容易出现问题 | 各自独立,互不影响 |
适用场景 | 不涉及动态内存分配的简单对象 | 含指针成员的复杂对象 |
示例 | 复制地址,仅共享数据 | 复制内容,独立内存 |
2.友元函数
友元函数(Friend Function)是一种特殊的函数,可以访问类的私有(private)和保护(protected)成员,即使它不是该类的成员函数。通常,在面向对象编程中,类的私有和保护成员只能被该类的成员函数或子类访问,而友元函数则打破了这个限制,允许非成员函数访问类的私有和保护成员。
友元函数的定义方式是在类内部使用关键字 friend
声明该函数
#include <iostream>
using namespace std;class Box {
private:int width;public:Box() : width(0) {}// 将 friend 关键字用于函数声明,使其成为友元函数friend void setWidth(Box &box, int w);
};// 友元函数定义
void setWidth(Box &box, int w) {// 可以直接访问 Box 类的私有成员 widthbox.width = w;
}int main() {Box box;setWidth(box, 10); // 调用友元函数return 0;
}
setWidth
是 Box
类的友元函数,虽然它不是 Box
类的成员函数,但可以直接访问 Box
类的私有成员 width
。
通过友元函数,可以实现:访问私有和保护成员:在特定情况下,让非成员函数或其他类可以访问类的私有和保护成员。