C++中赋值运算符、拷贝构造函数及相关概念详解
立即解锁
发布时间: 2025-08-19 01:36:34 阅读量: 1 订阅数: 6 


C++面向对象编程入门与实践
# C++ 中赋值运算符、拷贝构造函数及相关概念详解
## 1. 赋值运算符与继承
赋值运算符在运算符中较为特殊,它是不可继承的。如果在基类中重载了赋值运算符,派生类无法使用这个相同的函数。
## 2. 拷贝构造函数
### 2.1 定义与调用
可以使用两种语句来定义并初始化一个对象为另一个对象的值:
```cpp
alpha a3(a2); // 拷贝初始化
alpha a3 = a2; // 拷贝初始化,另一种语法
```
这两种定义方式都会调用拷贝构造函数,它会创建一个新对象,并将参数复制到新对象中。编译器会为每个对象自动提供默认的拷贝构造函数,它会进行成员逐个复制,这与赋值运算符的操作类似,但拷贝构造函数还会创建新对象。
### 2.2 示例代码
以下是一个重载赋值运算符和拷贝构造函数的示例:
```cpp
// xofxref.cpp
// 拷贝构造函数: X(X&)
#include <iostream>
using namespace std;
class alpha
{
private:
int data;
public:
alpha() // 无参构造函数
{ }
alpha(int d) // 单参构造函数
{ data = d; }
alpha(alpha& a) // 拷贝构造函数
{
data = a.data;
cout << "\n拷贝构造函数被调用";
}
void display() // 显示数据
{ cout << data; }
void operator = (alpha& a) // 重载赋值运算符
{
data = a.data;
cout << "\n赋值运算符被调用";
}
};
int main()
{
alpha a1(37);
alpha a2;
a2 = a1; // 调用重载的赋值运算符
cout << "\na2="; a2.display(); // 显示 a2
alpha a3(a1); // 调用拷贝构造函数
// alpha a3 = a1; // 等价的 a3 定义
cout << "\na3="; a3.display(); // 显示 a3
cout << endl;
return 0;
}
```
### 2.3 输出结果
```
赋值运算符被调用
a2=37
拷贝构造函数被调用
a3=37
```
`a2 = a1;` 调用赋值运算符,`alpha a3(a1);` 调用拷贝构造函数,`alpha a3 = a1;` 也可用于调用拷贝构造函数。
### 2.4 拷贝构造函数的其他调用场景
- **函数参数传递**:当对象按值传递给函数时,会调用拷贝构造函数创建函数操作的副本。例如:
```cpp
void func(alpha);
func(a1);
```
这里会调用拷贝构造函数创建 `a1` 对象的副本供 `func()` 使用。如果参数通过引用或指针传递,则不会调用拷贝构造函数。
- **函数返回值**:当函数返回一个值时,拷贝构造函数会创建一个临时对象。例如:
```cpp
alpha func();
a2 = func();
```
这里会调用拷贝构造函数创建 `func()` 返回值的副本,并将其赋值给 `a2`。
### 2.5 为何拷贝构造函数参数必须是引用
不能使用按值传递的方式作为拷贝构造函数的参数,例如 `alpha(alpha a)`,因为按值传递参数时会调用拷贝构造函数来创建副本,而这又会再次调用拷贝构造函数,导致无限递归,最终编译器会因内存耗尽而报错。所以,拷贝构造函数的参数必须通过引用传递,这样不会创建副本。
### 2.6 注意析构函数
在按值传递参数和返回值的情况下,函数返回时临时对象会被销毁,这会调用析构函数。如果没有预料到这种情况,可能会导致问题。因此,在处理需要非成员逐个复制的对象时,尽可能通过引用传递和返回,而不是按值传递。
### 2.7 同时定义拷贝构造函数和赋值运算符
通常在重载赋值运算符时,也需要重载拷贝构造函数,反之亦然。因为不希望在某些情况下使用自定义的复制例程,而在其他情况下使用默认的成员逐个复制方案。即使认为不会使用其中一个,编译器也可能在非明显的情况下使用它们,例如按值传递参数和返回值。如果类的构造函数涉及系统资源(如内存或磁盘文件)的使用,几乎总是需要重载赋值运算符和拷贝构造函数,并确保它们按预期工作。
### 2.8 禁止拷贝
有时可能需要禁止对象的拷贝操作。可以将赋值运算符和拷贝构造函数重载为私有成员:
```cpp
class alpha
{
private:
alpha& operator = (alpha&); // 私有赋值运算符
alpha(alpha&); // 私有拷贝构造函数
};
```
当尝试进行拷贝操作时,如 `a1 = a2;` 或 `alpha a3(a1);`,编译器会提示函数不可访问,且无需定义这些函数,因为它们永远不会被调用。
## 3. UML 对象图
UML 支持对象图,它用于描绘特定的对象,就像程序运行过程中的快照,代表特定时刻的对象。对象图是静态的 UML 图,可用于建模程序的特定行为。
### 3.1 对象表示
在对象图中,对象用矩形表示,与类图中的类表示方式类似。对象的名称、属性和操作以类似的方式显示,但对象的名称会有下划线。可以同时使用对象名和类名,用冒号分隔:`anObj:aClass`;如果不知道对象名称,也可以只使用类名,前面加冒号:`:aClass`。
### 3.2 链接与属性值显示
对象之间的线称为链接,表示对象之间的通信,还可以显示导航性。属性值可以用等号显示,如 `count = 0`,注意结尾没有分号,因为这是 UML 语法,不是 C++ 语法。
### 3.3 注释
UML 中的注释用带有折角的矩形表示,用于包含注释或解释。虚线将注释连接到图中的相关元素,注释可用于任何类型的 UML 图。
## 4. 内存高效的字符串类
### 4.1 自定义字符串类的缺陷
之前的自定义 `String` 类版本不够完善。如果重载 `=` 运
0
0
复制全文
相关推荐










