C++ Primer的样例代码,我改了改,又加了注释,记录一下。
注释里有我对输出结果的分析,重点看main函数里的注释。
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 行为像值的类
class HasPtr {
friend void swap(HasPtr&, HasPtr&);
friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { cout << "I'm constructor!" << endl; }
HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i){
cout << "I'm copy constructor!" << endl;
}
HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i)
{
cout << "I'm move constructor!" << endl;
p.ps = 0;
}
// 有意思的地方来了,这个拷贝赋值运算符同时也是一个移动赋值运算符
HasPtr& operator=(HasPtr rhs){
cout << "Operator = is running!" << endl;
// 使用临时变量是防范自赋值的情况的
// std::string *temp_ps = new std::string(*rhs.ps);
// delete ps;
// ps = temp_ps;
// i = rhs.i;
// return *this;
// 现在有swap函数了,一般有swap函数的都用swap实现赋值运算符
// 注意,参数不再是const引用了,而是用实参拷贝构造的临时对象
// 你会在上面的输出语句之前看到拷贝构造函数被调用了,就是这个原因
// 这种技术叫“拷贝并交换”,我愿称之为“二王换血”
// 因为交换后临时变量就带着没用的原值被销毁
swap(*this, rhs);
return *this;
}
~HasPtr() {// ps这个指针是这个类的成员,但指针指向的内容不是
cout << "I'm destructor!" << endl;
delete ps;// 所以析构默认只能析构这个指针本身,要想删内容,得手动写
}
private:
std::string *ps;
int i;
};
inline
void swap(HasPtr &lhs, HasPtr &rhs)
{
cout << "I am swapping!" << endl;
using std::swap;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);
}
bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
return *lhs.ps < *rhs.ps;
}
// 行为像指针的类
// class HasPtr {
// public:
// HasPtr(const std::string &s = std::string()):
// ps(new std::string(s)), i(0), use(new std::size_t(1)) { cout << "I'm constructor_"<< *use << "!" << endl; }
// HasPtr(const HasPtr &p):
// ps(p.ps), i(p.i), use(p.use){
// ++*use;
// cout << "I'm copy constructor_" << *use << "!" << endl;
// }
// HasPtr& operator=(const HasPtr&);
// ~HasPtr() {
// cout << "I'm destructor_" << *use << "!" << endl;
// if (--*use == 0) {
// delete ps;
// delete use;
// }
// }
// private:
// std::string *ps;
// int i;
// std::size_t *use;
// };
// HasPtr& HasPtr::operator=(const HasPtr &rhs)
// {
// cout << "Operator = is running!" << endl;
// ++*rhs.use;
// if (--*use == 0) {
// delete ps;
// delete use;
// }
// ps = rhs.ps;
// i = rhs.i;
// use = rhs.use;
// return *this;
// }
int main()
{
cout << "test begin" << endl;
HasPtr test(*new std::string("hi"));
cout << "test1 begin" << endl;
HasPtr test1 = test;
cout << "declare test2" << endl;
HasPtr test2;
cout << "definate test2" << endl;
// 和拷贝赋值一样,移动构造也会先创造一个临时变量,
// 再对临时变量使用移动赋值运算符,把临时变量交给test2
test2 = std::move(test1);
// 赋值结束,临时变量立刻被析构,可以看到下面这行打印前,先打印了一次析构
cout << "initialize a vector" << endl;
// 把test1注释掉是因为对它用了移动赋值,在上面对它调用std::move后,
// 语句结束时,程序就以为它被当成临时变量析构掉了,而实际上,还是在它的
// 生存周期结束时才会真正调用它的析构函数。但程序已经认为test1不能用了。
// 所以不注释的话,程序就自动退出且不报错
// vector<HasPtr> a{test,/*test1,*/test2};
// cout << "sorting" << endl;
// sort(a.begin(),a.end());
return 0;
}
以下是输出结果
test begin
I'm constructor!
test1 begin
I'm copy constructor!
declare test2
I'm constructor!
definate test2
I'm move constructor!
Operator = is running!
I am swapping!
I'm destructor!
initialize a vector
I'm destructor!
I'm destructor!
I'm destructor!
PS:markdown对于c++的代码段,得写cpp,而不是c++。