1、拷贝构造函数的第一个参数必须是引用类型,并且通常不应该是explicit的(因为可能会用到隐式转换)。
2、编译器合成拷贝构造函数用来阻止拷贝该类类型的对象,而是会将其参数成员逐个拷贝到创建的对象中。
Class Sales_data
{
public:
Sales_data(const Sales_data&);
private:
std::string bookNo;
int units_sold = 0;
double revenue =0.0;
};
Sales_data ::Sales_data(const Sales_data & oring):bookNo(oring.bookNo),units_sold(oring.unit_sold),revenue(oring.revenue){}
3、拷贝初始化发生情况:
4、在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。拷贝构造函数的参数必须是引用类型。
5、赋值运算符通常返回一个指向其左侧运算对象的引用。
6、合成拷贝赋值运算符通过成员类型的拷贝赋值运算符完成的。
7、析构函数首先调用函数体,按照成员初始化顺序的逆序销毁。
8、隐式销毁一个内置指针类型的成员不会delete它所指向的对象。
9、析构函数的调用:
10、当指向一个对象的引用或指针离开作用域时,析构函数不会执行。
11、对于一个类来讲,拷贝构造函数、拷贝赋值运算符、析构函数(移动构造函数、移动赋值运算符)是三位一体(五位一体)的。
12、如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。
13、我们可以对具有合成版本的成员函数使用=default(即,默认构造函数或拷贝控制运算符)
14、阻止拷贝
struct NoCopy
{
NoCopy()=default;
NoCopy(const NoCopy&) = delete;//阻止拷贝
NoCopy &operator=(const NoCopy&)=delete;//阻止赋值
~NoCopy()=default;//使用合成的析构函数
}
15、可以对任何函数指定delete,但是只能对编译器可以合成的默认构造函数或拷贝控制成员使用=default;
16、不能删除析构函数。
17、当不可能拷贝、赋值或销毁类的成员时,类的合成拷贝控制成员就被定义为删除的。
18、通常管理类外资源的类必须定义拷贝控制成员。
19、行为像值的类:
class HasPtr
{
public:
HasPtr(const std::string &s = std::string()):ps(new std::string(s),i(0)){}
HasPtr(const HasPtr &p): ps(new std::string(*p.ps)),i(p.i){}
HasPtr& operatro=(const HasPtr &);
~HasPtr(){delete ps;}
private:
std::stirng *ps;
int i;
}
HasPtr& HasPtr::operatro=(const HasPtr & rhs)
{
auto newp = new string(*rhs.ps);
delete ps;
ps = newp;
i= rhs.i;
return *this;
}
20、令一个类展现类似指针的行为最好是使用shared_ptr来管理类中的资源。
#include
#include
using namespace std;
class Message
{
friend class Folder;
public:
explicit Message(const std::string & str = "") :contents(str){}
Message(const Message&);
Message& operator =(const Message &);
~Message();
//-----------------从给定的Folders集合中添加删除Message-----------------------------------------
void save(Folder&);
void remove(Folder&);
private:
std::string contents;
std::setfolders;
void add_to_Folders(const Message&);//将一个message添加到一组folders中
void remove_form_Folders();//将一个message从所有的folders中删除
};
void Message::save(Folder & f)
{
folders.insert(&f);
f.addMsg(this);
}
void Message::remove(Folder &f)
{
folders.erase(&f);
f.remMsg(this);
}
void Message::add_to_Folders(const Message &m)
{
for (auto f : m.folders)
{
f->addMsg(this);
}
}
Message::Message(const Message & m) :contents(m.contents), folders(m.folers)
{
add_to_Folders(m);
}
21、右值引用只能绑定到一个即将被销毁的对象(引用就是一段内容暂时不能更改的内存的指针)
22、右值要么是字面常量、要么是在表达式求值过程中创建的临时对象。
23、可以调用std::move()获得绑定到左值上的右值引用。
int && rr3 = std::move(rr1);
24、移动构造函数不分配任何新内存,由于移动操作不分配认可资源。因此,移动操作通常不会抛出任何异常。
25、在一个构造函数中noexpect出现在参数列表和初始化列表开始的冒号之间。
class StrVec
{
public:
StrVec(StrVec&&) noexcept;
};
StrVec::StrVec(StrVec&& s) noexcept:{}
26、移动赋值运算符与析构函数相同的工作。
StrVec & StrVec::operator=(StrVec && rhs) noexcept
{
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;
}
27、当我们编写一个移动操作时,必须确保移后源对象进入一个可析构的状态。(通过将移后源对象的指针成员置为nullptr)
移动操作后,移后源对象必须保持有效可析构的状态,用户不能对其值进行任何假设。
28、如果一个类没有移动操作,通过正常的函数匹配,类会使用对应的拷贝操作代替移动操作。
29、如果一个类定义了一个移动构造函数和/或一个移动赋值运算符,则该类的合成拷贝构造函数和拷贝赋值运算符会被定义为删除的。
30、用拷贝构造函数代替移动构造函数几乎肯定是安全的。
31、区分移动和拷贝的重载函数通常有一个版本接受一个const T&,而另外一个版本接受一个T&&。
32、如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符。
28、一个函数可以同时用const和引用限定。