class Foo {
public:
Foo(); // 默认构造函数
// 它在几种情况下都会被隐式地使用,因此,拷贝构造函数通常不应该是explicit的。
Foo(const Foo&); // 拷贝构造函数
// ...
};
class Data {
public:
Data(const Data&);
private:
std::string bookNo;
int units_sold = 0;
double revenue = 0.0;
};
Data::Data(const Data& orig): bookNo(orig.bookNo), units_sold(orig.units_sold), revenue(orig.revenue) {}
string dots(10, ‘.‘); // 直接初始化
string s(dots); // 直接初始化
string s2 = dots; // 拷贝初始化
sting null_book = "9-999999-99"; // 拷贝初始化
string nines = string(100, ‘9‘); // 拷贝初始化
operator=
的函数class Foo {
public:
Foo& operator=(const Foo&); // 赋值运算符
};
// 等价于合成拷贝赋值运算符
Data& Data::operator=(const Data& rhs) {
bookNo = res.bookNo; // 调用string::operator=
units_sold = res.units_sold; // 使用内置的int赋值
revenue = res.revenue; // 使用内置的double赋值
return *this;
}
delete运算符
时被销毁。{ // 新作用域
// p 和 p2 都指向动态分配的对象
Data *p = new Data; // p 是要给内置指针
auto p2 = make_shared<Data>(); // p2 是一个智能指针
Data item(*p); // 拷贝构造函数将*p拷贝到item中
vector<Data> vec; // 局部对象
vec.push_back(*p2); // 拷贝p2指向的对象
delete p; // 对p指向的对象执行析构函数
}
// 退出局部作用域,对item、p2和vec调用析构函数
=default
来显式地要求编译器生成合成地版本。class Data {
public:
// 拷贝控制成员,使用default
Data() = default;
Data(const Data&) = default;
Data& operator=(const Data&);
~Data() = default;
};
Data& Data::operator=(const Data&) = default;
=default
。=default
,即默认构造函数或拷贝控制成员。=delete
。虽然声明了它们,但是不能以任何方式使用它们。struct NoCopy {
NoCopy() = default; // 使用合成的默认构造函数
NoCopy(cosnt NoCopy&) = delete; // 阻止拷贝
NoCopy &operator=(const NoCopy&) = delete; // 阻止赋值
~NoCopy() = default; // 使用合成的析构函数
};
// 行为像值
class HasPtr {
public:
HasPtr(const string &s = string()): ps(new string(s)), i(0) {}
HasPtr(const HasPtr &p): ps(new string(*p.ps)), i(p.i) {} // 对ps指向的string,每个HasPtr对象都有自己的拷贝
HasPtr& operator=(const HasPtr &);
~HasPtr() { delete ps;}
private:
string *ps;
int i;
};
// 注意:
// 1. 如果将一个对象赋予它自身,赋值运算符必须能正确工作。
// 2. 大多数赋值运算符组合了析构函数和拷贝构造函数的工作。
// 一个好的方法:在销毁左侧运算对象资源之前拷贝右侧运算对象。
HasPtr& HasPtr::operator=(const HasPtr &rhs) {
auto newp = new string(*rhs.ps); // 拷贝底层string
delete ps; // 释放旧内存
ps = newp; // 从右侧运算对象拷贝数据到本对象
return *this; // 返回本对象
}
// 行为像指针
// 注意:
// 1. 令一个类展现类似指针的行为的最好方法是使用shared_ptr来管理类中的资源。
// 2. shared_ptr会自动管理资源,有时希望直接管理资源,这种情况下,就得使用 引用计数 了。
// 引用计数的工作方式:
// 1. 除了初始化对象外,每个构造函数(拷贝构造函数除外)还有创建一个引用计数,用来记录有多少对象与正在创建的对象共享状态。当创建一个对象时,只有一个对象共享状态,因此将计数器初始化为1。
// 2. 拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定对象的状态又被一个新用户所共享。
// 3. 析构函数递减计数器,指出共享状态的用户又少了一个。如果计数器变为0,则析构函数释放状态。
// 4. 拷贝赋值运算符递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器变为0,意味着它的共享状态没有用户了,拷贝赋值运算符就必须销毁状态。
class HasPtr {
public:
// 构造函数分配新的string和新的计数器,将计数器置为1
HasPtr(const string &s = string()): ps(new string(s)), i(0), use(size_t(1)) {}
// 拷贝构造函数拷贝三个数据成员,并递增计数器
HasPtr(const HasPtr &p): ps(p.ps), i(p.i), use(p.use) {++*use;}
//
HasPtr& operator=(const HasPtr&);
~HasPtr();
private:
string *ps;
int i;
size_t *use; // 用来记录有多少对象共享*ps的成员
};
HasPtr::~HasPtr() {
// 不能无条件的delete
if(--*use == 0) { // 如果引用计数变为0
delete ps; // 释放string内存
delete use; // 释放计数器内存
}
}
HasPtr& HasPtr::operator=(const HasPtr$ rhs) {
++*rhs.use; // 先递增右侧运算符对象的引用计数
if(--*use == 0) { // 然后递减本对象的引用计数
delete ps; //
delete use;
}
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this; // 返回本对象
}
swap
的函数。swap
,而不是 std::swap
。与拷贝控制成员不同,swap并不是必要的。但是,对于分配了资源的类,定义swap可能是一种很重要的优化手段。class HasPtr{
friend void swap(HasPtr&, HasPtr&);
// ...
HasPtr& operator=(HasPtr );
};
inline void swap(HasPtr &lhs, HasPtr& &rhs) {
using std::swap;
swap(lhs.ps, rhs.ps); // 交换指针,而不是string数据,减少不必要的内存分配
swap(lhs.i, rhs.i);
}
// 拷贝并交换,将左侧运算对象与右侧运算对象的一个副本进行交换
HasPtr& HasPtr::operator=(HasPtr rhs){
// 注意rhs是按值传递的,意味着HasPtr的拷贝构造函数将右侧运算对象中的string拷贝到rhs
swap(*this, rhs); // rhs现在指向本对象曾经使用的内存
return *this; // rhs被销毁,从而deleter了rhs中的指针
}
需求描述:两个类命名为Message和Folder,分别表示电子邮件(或者其他类型的)消息和消息目录。每个Message对象可以出现在多个Folder中。但是,任意给定的Message的内容只有一个副本。这样,如果一条Message的内容被改变,则从它所在的任何Folder俩浏览此Message时,都会看到改变后的内容。
待整理...
&&
来获得右值引用。int &&rr2 = std::move(rr1);
// 移动操作不应该抛出任何异常
StrVec::StrVec(StrVec &&s) noexcept: elements(s.elements), first_free(s.first_free), cap(s.cap) {
//
s.elements = s.first_free = s.cap = nullptr;
}
StrVec &StrVec::operator=(StrVec &&rhs) noexcept {
// 直接检测自赋值
if (this != &rhs) {
free(); // 释放已有元素
elements = rhs.elements; // 从rhs接管资源
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr; // 将rhs置为可析构状态
}
return *this;
}
const T&
,而另一个版本接受一个 T&&
。原文:https://www.cnblogs.com/parzulpan/p/13463327.html