![e01c98c3e018e42e63324b25b6cabd34.png](https://img-blog.csdnimg.cn/img_convert/e01c98c3e018e42e63324b25b6cabd34.png)
本文主要介绍 C++ 中使用容器和拥有指针作为属性的类容易遇到的问题,以及 C++11 新的解决思路
在 C++03 中使用带指针属性的类
假如我们定义了这样一个类
class Person {
public:
Person(const string& name) {
pName = new string(name);
}
~Person() {delete pName;}
string *pName;
};
我们希望把这个类放到 vector
里面,于是写出了一段看上去似乎没问题,但事实上会崩溃的代码
int main() {
vector<Person> persons;
/*
这个 push_back 会发生三件事情:
1. 构造一个 pName 指向 "Bob" 的 Person
2. 讲这个对象浅拷贝到 persons[0] 位置
3. 析构生成 1 中生成的 Person, 释放"Bob"
*/
persons.push_back(Person("Bob"));
return 0;
}
// persons 析构
// 已被释放的 "Bob" 再次被释放,程序崩溃
为了避免这个问题,在 C++03 中,常见的解决方法是给 Person 添加一个拷贝构造函数
Person(const Person& rhs) {
pName = new string(*rhs.pName);
}
然而,使用这种方法时,"Bob" 被多余地构造和释放了一次,造成了浪费
C++11 的解决方法
C++11 中,解决这个问题最简单的方法是,使用 emplace_back
替换 push_back
,让容器自己来构造对象
int main() {
vector<Person> persons;
// 在 persons 内部生成 Bob
persons.emplace_back("Bob");
return 0;
}
C++11 中,各种容器的插入,都实现了 emplace 版本
然而,如果我们一定要使用 push_back
,注意到 Person("Bob")
是个右值,由于 C++11 重载了右值版本的 push_back(T&&)
,我们给 Person
添加移动构造函数
Person(Person&& rhs) {
pName = rhs.pName;
rhs.pName = nullptr;
}
这样,push_back
内部只会发生两次指针赋值操作,而不是重新构造一个字符串
那么,使用智能指针能不能解决这个问题呢?答案是可以的,两种解决方法
// 1. 使用 shared_ptr
class Person {
public:
Person(const string& name):
pName(new string(name)) {}
shared_ptr<string> pName;
};
// 2. 使用 unique_ptr
class Person {
public:
Person(const string& name):
pName(new string(name)) {}
unique_ptr<string> pName;
};
要注意的是, 如果使用 shared_ptr
,每次拷贝之后 pName
指向的字符串事实上是共享的;如果使用 unique_ptr
,则 Person
是不可拷贝只可移动的
本文主要翻译自 Bo Qian 的 YouTube 视频
C++ 11: Resource Managing Classyoutu.be