标题取的不太恰当,实际是:对象浅拷贝的陷阱。
将局部对象变量放入容器中进行管理需要特别小心(存在重复析构成员变量为指针类型的属性),直接上代码看:
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Person {
public:
Person(string name) : pName_(new string(name)) {}
// Person(const Person& person);
~Person() {
cout << *pName_ << " deleted" << endl;
delete pName_;
}
void printName() { cout << *pName_ << endl; }
private:
string* pName_;
};
int main() {
vector<Person> persons;
Person p("George");
persons.push_back(p);
persons.front().printName();
cout << "Goodbye " << endl;
return 0;
}
在main函数return前:
1.向量对象persons会被析构,并且其中的元素也会调用对应的析构函数进行析构(调用~Person);
2.局部对象p也会被析构;
初看好像没有什么问题,但是局部对象b会被编译器默认的拷贝构造函数(Copy constructer)浅拷贝一份放入persons向量中。如此b对象的string *pName_指针同时被2个对象拥有,那么就存在~Person()重复delete pName_的现象!
在C++11版本中如何处理这个问题?
使用移动构造函数 + 智能指针: 效率高,不用重复创建pName_ 。
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Person {
public:
Person(string name) : pName_(new string(name)) {}
Person(const Person& person) = delete;
~Person() { }
Person(Person&&) = default;
void printName() { cout << *pName_ << endl; }
private:
std::unique_ptr<string> pName_; // 使用shared_ptr有点浪费
};
int main() {
vector<Person> persons;
Person p("George");
persons.push_back(std::move(p));
// p 对象被Move Construct,已经不能使用
persons.front().printName();
cout << "Goodbye " << endl;
return 0;
}
在C++03版本中如何处理这个问题?
不采用编译器提供的默认“拷贝构造函数、赋值构造函数”,类中存在指针属性,直接使用默认的这2个构造函数是非常危险的。直接显示编写这2个构造函数!
效率没有C++11版高(这就体现了移动构造的高效率),其他无差异。
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Person {
public:
Person(string name) : pName_(new string(name)) {}
Person(const Person& person){
this->pName_ = new string(*person.pName_);
}
~Person() {
cout << *pName_ << " deleted" << endl;
delete pName_;
}
Person(Person&&) = default;
void printName() { cout << *pName_ << endl; }
private:
string* pName_;
};
int main() {
vector<Person> persons;
Person p("George");
persons.push_back(p); // 使用自定义的拷贝构造函数深度复制p对象
persons.front().printName();
cout << "Goodbye " << endl;
return 0;
}