文章目录
- 概述
- 13.1拷贝、赋值与销毁
- 合成拷贝构造函数
- 拷贝初始化
- 参数和返回值
- 拷贝初始化的限制
- 编译器可以绕过拷贝构造函数
- 拷贝运算符
- 析构函数
- 三/五原则
- 使用=default
- 阻止拷贝
- 合成的拷贝控制成员可能是删除的
- private拷贝控制
- 拷贝控制和资源管理
- 行为像值的类
- 类值拷贝赋值运算符
- 定义行为像指针的类
- 引用计数
- 定义一个引用计数的类
- 类指针的拷贝成员“篡改” 引用计数
- 交换操作
- 拷贝控制示例
- 动态内存管理
- 对象移动
- 右值引用
- 移动构造函数和移动赋值运算符
- 移动迭代器
概述
13.1拷贝、赋值与销毁
合成拷贝构造函数
拷贝初始化
参数和返回值
拷贝初始化的限制
编译器可以绕过拷贝构造函数
拷贝运算符
析构函数
三/五原则
使用=default
阻止拷贝
### 析构函数不能是删除成员
合成的拷贝控制成员可能是删除的
private拷贝控制
在新标准发布之前 ,类是通过将其拷贝构造函数和拷贝赋值运算符芦明为 pr ivate
的来阻止拷贝:
class Priva teCopy {
//元访问说明符 ;接下来的成员默认为p rivate 的;参见 7.2 节 ( 第 240 页 )
//拷贝控制成员是 priv a te 的,因此普通用户代码无法访问
Priva teCopy ( const Priva teCopy& ) ;
PrivateCopy & operator= ( const Priva teCopy& ) ;
//其他成员
public :
Pr ivateCopy ( ) = def au lt ; //使用合成的默认构造函数
~PrivateCopy ( ) ; // 用尸可以定义此类型的对象 ,但无法拷贝它们
由于析构函数是 public 的,用户可以定义 Pr ivateCopy 类型的对象 。但是 ,由于拷贝 构造函数和拷贝赋值运算符是 pr iva te 的,用户代码将不能拷贝这个类型的对象
。但是, 友元和成员函数仍旧可以拷贝对象 。为了阻止友元和成员函数进行拷贝,我们将这些拷贝 控制成员声明为 pr iva t e 的,但并不定义它们 。
声明但不定义 个成员函数是合法的 ( 参见 6. 1.2 节,第 186 页),对此只有一个例外 , 我们将在 15.2.1 节 (第 528 页) 中介绍。试图访问一个未定义的成员将导致
一个链接时错 误。通过声明 (但不定义) pr ivate 的拷贝构造函数 ,我们可以预先阻止任何拷贝该类 型对象的企图 :试图拷贝对象的用户代码将在编译阶段被标记为错 误:成员函数或友元函
数中的拷贝操作将会导致链接时错误 。
拷贝控制和资源管理
行为像值的类
类值拷贝赋值运算符
定义行为像指针的类
引用计数
定义一个引用计数的类
类指针的拷贝成员“篡改” 引用计数
交换操作
拷贝控制示例
动态内存管理
对象移动
右值引用
移动构造函数和移动赋值运算符
#pragma once
#include <iostream>
class HasPtr
{friend void swap(HasPtr&, HasPtr&);
public://默认构造函数HasPtr(const std::string& s = std::string()) : ps(new std::string(s)), i(0){}//拷贝构造函数HasPtr(const HasPtr& data) : ps(new std::string(*(data.ps))), i(data.i){}//添加都移动构造函数HasPtr(HasPtr&& P)noexcept :ps(P.ps), i(P.i) {P.ps = nullptr;P.i = 0;}HasPtr& operator=(HasPtr rhs){swap(*this, rhs);return *this;}// 析构函数~HasPtr(){delete ps;}
private:std::string* ps;int i;
};inline void swap(HasPtr& lhs, HasPtr& rhs)
{using std::swap;swap(lhs.ps, rhs.ps); // swap the pointers, not the string dataswap(lhs.i, rhs.i); // swap the int members
}
int main()
{HasPtr data1, data2("123"), data3, data4("abc");data1 = data2;//拷贝语义data3 = std::move(data4);//移动语义
}
所有五个拷贝控制成员应该看作一个整体:一般来说,如果一个类定义了任何一个拷贝操作,它应该定义所有五个操作。如前所述,某些类必须定义拷贝构造函数、拷贝赋值运算符和析构函数。这些类只有一个资源,而拷贝成员必须拷贝此资源。
移动迭代器
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <algorithm>
#include <cassert>class StrVec {
public:// 默认构造函数StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {std::cout << "Default constructor called." << std::endl;}// 拷贝构造函数StrVec(const StrVec& s) {std::cout << "Copy constructor called." << std::endl;auto newdata = alloc_n_copy(s.begin(), s.end());elements = newdata.first;first_free = cap = newdata.second;}// 移动构造函数StrVec(StrVec&& s) noexcept : elements(s.elements), first_free(s.first_free), cap(s.cap) {std::cout << "Move constructor called." << std::endl;s.elements = s.first_free = s.cap = nullptr;}// 拷贝赋值运算符StrVec& operator=(const StrVec& rhs) {std::cout << "Copy assignment operator called." << std::endl;auto data = alloc_n_copy(rhs.begin(), rhs.end());free();elements = data.first;first_free = cap = data.second;return *this;}// 移动赋值运算符StrVec& operator=(StrVec&& rhs) noexcept {std::cout << "Move assignment operator called." << std::endl;if (this != &rhs) {free();elements = rhs.elements;first_free = rhs.first_free;cap = rhs.cap;rhs.elements = rhs.first_free = rhs.cap = nullptr;}return *this;}// 析构函数~StrVec() {std::cout << "Destructor called." << std::endl;free();}// 拷贝版本的 push_backvoid push_back(const std::string& s) {std::cout << "Copy push_back called with: " << s << std::endl;chk_n_alloc();alloc.construct(first_free++, s);}// 移动版本的 push_backvoid push_back(std::string&& s) {std::cout << "Move push_back called with: " << s << std::endl;chk_n_alloc();alloc.construct(first_free++, std::move(s));}// 返回元素数量size_t size() const { return first_free - elements; }// 返回容量size_t capacity() const { return cap - elements; }// 返回起始迭代器std::string* begin() const { return elements; }// 返回结束迭代器std::string* end() const { return first_free; }// 左值版本的 sorted 方法void sorted() & {std::cout << "Lvalue sorted called." << std::endl;std::sort(begin(), end());}// 右值版本的 sorted 方法StrVec sorted() && {std::cout << "Rvalue sorted called." << std::endl;std::sort(begin(), end());return std::move(*this);}private:std::allocator<std::string> alloc;// 检查是否需要重新分配内存void chk_n_alloc() {if (size() == capacity()) {reallocate();}}// 分配并复制元素std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);// 释放内存void free();// 重新分配内存void reallocate();std::string* elements;std::string* first_free;std::string* cap;
};// 分配并复制元素的实现
std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string* b, const std::string* e) {auto data = alloc.allocate(e - b);return {data, std::uninitialized_copy(b, e, data)};
}// 释放内存的实现
void StrVec::free() {if (elements) {for (auto p = first_free; p != elements; ) {alloc.destroy(--p);}alloc.deallocate(elements, cap - elements);}
}// 重新分配内存的实现
void StrVec::reallocate() {std::cout << "Reallocating memory..." << std::endl;auto newcapacity = size() ? 2 * size() : 1;auto newdata = alloc.allocate(newcapacity);auto dest = newdata;auto elem = elements;for (size_t i = 0; i != size(); ++i) {alloc.construct(dest++, std::move(*elem++));}free();elements = newdata;first_free = dest;cap = elements + newcapacity;
}// 打印 StrVec 中的元素
void printStrVec(const StrVec& vec) {for (auto it = vec.begin(); it != vec.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;
}// 测试默认构造函数
void testDefaultConstructor() {StrVec vec;assert(vec.size() == 0);std::cout << "Default constructor test passed." << std::endl;
}// 测试拷贝构造函数
void testCopyConstructor() {StrVec vec1;vec1.push_back("apple");vec1.push_back("banana");StrVec vec2(vec1);assert(vec2.size() == vec1.size());auto it1 = vec1.begin();auto it2 = vec2.begin();while (it1 != vec1.end() && it2 != vec2.end()) {assert(*it1 == *it2);++it1;++it2;}std::cout << "Copy constructor test passed." << std::endl;
}// 测试移动构造函数
void testMoveConstructor() {StrVec vec1;vec1.push_back("cherry");vec1.push_back("date");StrVec vec2(std::move(vec1));assert(vec1.size() == 0);assert(vec2.size() == 2);std::cout << "Move constructor test passed." << std::endl;
}// 测试拷贝赋值运算符
void testCopyAssignmentOperator() {StrVec vec1;vec1.push_back("elderberry");vec1.push_back("fig");StrVec vec2;vec2 = vec1;assert(vec2.size() == vec1.size());auto it1 = vec1.begin();auto it2 = vec2.begin();while (it1 != vec1.end() && it2 != vec2.end()) {assert(*it1 == *it2);++it1;++it2;}std::cout << "Copy assignment operator test passed." << std::endl;
}// 测试移动赋值运算符
void testMoveAssignmentOperator() {StrVec vec1;vec1.push_back("grape");vec1.push_back("honeydew");StrVec vec2;vec2 = std::move(vec1);assert(vec1.size() == 0);assert(vec2.size() == 2);std::cout << "Move assignment operator test passed." << std::endl;
}// 测试 push_back 方法
void testPushBack() {StrVec vec;vec.push_back("apple");vec.push_back(std::string("banana"));assert(vec.size() == 2);std::cout << "Push_back test passed." << std::endl;
}// 测试 sorted 方法(左值)
void testSortedLvalue() {StrVec vec;vec.push_back("banana");vec.push_back("apple");vec.push_back("cherry");vec.sorted();auto it = vec.begin();assert(*it == "apple");++it;assert(*it == "banana");++it;assert(*it == "cherry");std::cout << "Sorted (lvalue) test passed." << std::endl;
}// 测试 sorted 方法(右值)
void testSortedRvalue() {StrVec sortedVec = StrVec().push_back("grape").push_back("date").push_back("elderberry").sorted();auto it = sortedVec.begin();assert(*it == "date");++it;assert(*it == "elderberry");++it;assert(*it == "grape");std::cout << "Sorted (rvalue) test passed." << std::endl;
}int main() {testDefaultConstructor();testCopyConstructor();testMoveConstructor();testCopyAssignmentOperator();testMoveAssignmentOperator();testPushBack();testSortedLvalue();testSortedRvalue();std::cout << "All tests passed!" << std::endl;return 0;
}
}