一、C++特殊成员函数简介
- C++自动为类生成的特殊成员函数包括:
- 默认构造函数:无参数创建对象。
- 拷贝构造函数:以另一个现有对象为模板创建新对象。
- 拷贝赋值操作符:用另一个现有对象替换现有对象的内容。
- 析构函数:对象离开作用域时被销毁。
 
1.1 函数调用示例
在以下示例中,每行代码调用的特殊成员函数是:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | MyVector<int> function(MyVector<int> vec0) { MyVector<int> vec1;
 MyVector<int> vec2{3, 4, 5};
 MyVector<int> vec3();
 MyVector<int> vec4(vec2);
 MyVector<int> vec5{};
 MyVector<int> vec6{vec3 + vec4};
 MyVector<int> vec7 = vec4;
 vec7 = vec2;
 return vec7;
 }
 
 | 
1.2 拷贝构造函数
拷贝构造函数负责创建对象的深拷贝,必要时进行深度复制。
| 12
 3
 4
 5
 
 | StringVector::StringVector(const StringVector &other): _logicalSize(other._logicalSize), _allocatedSize(other._allocatedSize) {
 _elems = new ValueType[_allocatedSize];
 std::copy(other.begin(), other.end(), begin());
 }
 
 | 
1.3 拷贝赋值操作符
拷贝赋值操作符需释放对象当前资源后,执行拷贝。注意自赋值情况。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | StringVector& StringVector::operator=(const StringVector &rhs) {if (this != &rhs) {
 delete[] _elems;
 _logicalSize = rhs._logicalSize;
 _allocatedSize = rhs._allocatedSize;
 _elems = new ValueType[_allocatedSize];
 std::copy(rhs.begin(), rhs.end(), begin());
 }
 return *this;
 }
 
 | 
1.4 删除操作
通过显式删除特殊成员函数,可以阻止对象被拷贝。
| 12
 3
 4
 5
 6
 7
 
 | class LoggedVector {public:
 LoggedVector(int num, int denom);
 ~LoggedVector();
 LoggedVector(const LoggedVector &rhs) = delete;
 LoggedVector &operator=(const LoggedVector &rhs) = delete;
 };
 
 | 
1.5 三法则与零法则
- 三法则:如果你需要自定义析构函数、拷贝构造函数或拷贝赋值操作符中的任何一个,你可能需要定义所有三个,因为这意味着你的类有资源管理需求。
- 零法则:如果默认操作符满足需求,最好不要自定义这些特殊成员函数。
二、拷贝问题与优化
在C++中,频繁的对象拷贝可能导致性能问题。考虑以下代码片段,它演示了在一个简单场景中对象如何被创建和拷贝:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | StringVector findAllWords(const string &filename) {StringVector words;
 
 return words;
 }
 
 int main() {
 StringVector words;
 words = findAllWords("words.txt");
 
 }
 
 | 
在没有编译器优化的情况下,这段代码会导致多次StringVector对象的创建和拷贝:
- main函数中声明的- words对象通过默认构造函数创建。
- findAllWords函数中的- words对象通过默认构造函数创建。
- findAllWords返回时,通过拷贝构造函数创建一个临时对象。
- 将临时对象赋值给main中的words对象,调用拷贝赋值操作符。

为了减少不必要的对象拷贝,C++提供了几种优化机制:
2.1 拷贝省略与返回值优化(RVO)
编译器可以优化掉一些不必要的拷贝,特别是函数返回时的临时对象。这种优化称为返回值优化(RVO),直接在调用方的空间创建返回对象,避免额外的拷贝。现代C++编译器智能地应用RVO,减少性能损耗。

2.2 移动语义
C++11引入的移动语义允许对象的资源“转移”而非传统意义上的拷贝。通过使用移动构造函数和移动赋值操作符,可以有效地将一个对象的状态或资源转移到另一个对象,从而避免深度拷贝带来的开销。
例如:
| 1
 | StringVector words = findAllWords("words.txt");
 | 
如果findAllWords返回的对象利用移动语义,那么这里将不会发生深度拷贝,而是资源的转移。
更详细的移动语义后续讲解。