我有一个用原始指针实现的双向对象关联,它工作起来没有缺陷。然后我决定用智能指针重构我的代码,突然其中一个类(Department)的字符串成员(depName。我检查了参数是否正确传递,并且在构造函数中一切正常。成员获得了预期值。但后来它就不再可访问了,除了之前运行顺利的代码,现在也崩溃了。我不知道为什么。
EDIT:在Manager类中使用的字符串变量名似乎也发生了同样的情况。结束编辑
- 代码编译时没有错误,甚至没有警告
- 我正在使用qt创建者3.3.0和mingw编译器5.4.0
- 系统声称存在"分段故障"
这是我的代码(很抱歉,它太多了——我尽我所能减少了它):
在头文件中:
class Manager;
class Department
{
private:
string depName;
shared_ptr<Manager> head;
vector <shared_ptr<Manager>> depMembers;
public:
Department(string depName, shared_ptr<Manager> head);
virtual ~Department();
string getDepName() const;
void setDepName(const string &value);
void addMember(shared_ptr<Manager> newMember);
void removeMember(shared_ptr<Manager> who);
const shared_ptr<Manager>getHead() const;
void setHead(shared_ptr<Manager>value);
double sumOfIncome();
void show();
};
//--------------------------------
class Department;
class Manager
{
private:
string name;
float salary;
float bonus;//Bonus ist ein Prozentsatz
weak_ptr<Department> myDepartment;
// Department * myDepartment; //With this raw pointer the code still worked*
public:
Manager(string, float, float);
virtual ~Manager();
float income()const ;
string toString()const ;
double calcBonus() const;
shared_ptr<Department> getMyDepartment() const;
void setMyDepartment(shared_ptr<Department> abt);
float getSalary() const;
string getName() const;
};
在cpp文件中:部门.cpp
//---------------------------------------------------
Department::Department(string depName, shared_ptr<Manager>head)
:depName(depName),head(nullptr)
{
this->setHead(head);
cout << "nIn constructor parameter depName: " + depName;
cout << "n instancevariable " + this->depName << endl;
}
//--------------------------------
Department::~Department()
{}
//--------------------------------
string Department::getDepName() const
{
return this->depName;
}
//--------------------------------
void Department::setDepName(const string &value)
{
depName = value;
}
//--------------------------------
void Department::addMember(shared_ptr<Manager> newMember)
{
depMembers.push_back(newMember);
}
//--------------------------------
void Department::removeMember(shared_ptr<Manager> who)
{
vector<shared_ptr<Manager>>::iterator itMember = depMembers.begin();
//Iterator must be dereferenced to access data
while( *itMember != who){
itMember++;
}
if( *itMember == who)
depMembers.erase( itMember);
}
//--------------------------------
const shared_ptr<Manager> Department::getHead() const
{
return head;
}
//--------------------------------
void Department::setHead(shared_ptr<Manager>value)
{
if( head != nullptr && head->getMyDepartment()!= nullptr)
head->setMyDepartment(nullptr);//department of old head is deleted
//new head of department assigned
head = value;
//bidirektionaler access
if(head !=nullptr)
head->setMyDepartment( shared_ptr<Department>(this));
}
//--------------------------------
double Department::sumOfIncome()
{
double sum = 0;
for(unsigned int i=0; i < depMembers.size(); i++){
sum += depMembers[i]->getSalary() ;
}
return sum;
}
//--------------------------------
void Department::show()
{
cout <<"----------------" << endl;
cout << "Department: " << this->depName << " run by " << head->getName()<<endl;
cout <<"----------------" << endl;
cout << "Members: " << endl;
cout <<"----------------" << endl;
cout << head->toString() << endl;
for( unsigned int i=0; i < depMembers.size() ; i++){
cout <<"----------------" << endl;
cout << depMembers[i]->toString()<< endl;
}
cout <<"----------------" << endl;
}
管理员.cpp
//---------------------
float Manager::getSalary() const
{
return salary;
}
//----------------------------------
string Manager::getName() const
{
return name;
}
//----------------------------------
Manager::Manager(string n, float s, float bon)
:name(n),salary(s), bonus(bon)
{}
//----------------------------------
Manager::~Manager(){}
//----------------------------------
float Manager::income()const
{
return (salary + calcBonus() );
}
//----------------------------------
string Manager::toString() const
{
stringstream ss;
ss << name << "n heads the department ";
shared_ptr<Department> dep = myDepartment.lock();
if( dep !=nullptr)
ss<< dep->getDepName();
else ss << " NONE ";
ss << "nBonus: " << calcBonus();
ss << "nIncome: " << income();
return ss.str();
}
//----------------------------------
double Manager::calcBonus()const
{
shared_ptr<Department> dep = myDepartment.lock();
if(dep != nullptr)
return dep->sumOfIncome()* bonus;
else
return 0;
}
//----------------------------------
shared_ptr<Department> Manager::getMyDepartment() const
{
// if( !meineAbteilung->expired())
return myDepartment.lock();
}
//----------------------------------
void Manager::setMyDepartment( shared_ptr<Department> dep)
{
myDepartment = dep;
}
//----------------------------------
试运行:
int main(){
shared_ptr<Department> itDepartment
= make_shared<Department>("IT",make_shared<Manager>("Julia", 66066, 0.15));
itDepartment->show();
return 0;
}
它崩溃了,因为没有人拥有某些对象。这是一个巨大的危险信号:
head->setMyDepartment(shared_ptr<Department>(this));
有enable_shared_from_this
这样的东西,但你没有使用它,所以从this
构建shared_ptr
是无稽之谈,特别是因为你允许它立即超出范围。这将调用你不想要的delete this
。
需要有人从外部拥有这些物品。他们不能仅仅拥有彼此(循环引用)。
我可以将John Zwinck的答案标记为正确-(谢谢John!),但我自己需要更多信息来理解这个问题,所以我决定发布自己的答案。
我的方法有两个问题:
-
shared_ptr
永远不应该由原始指针变量生成,因为每次从原始指针初始化shared_ptr
都会创建一个具有自己引用计数的新管理器对象。只有根据现有管理器对象的shared_ptr
或weak_ptr
之一创建新的shared_ptr
时,才会使用现有管理器。(不知怎么的,我读的书设法省略了这一重要信息。)this
是一个原始指针,所以在我的Department构造函数中创建了第二个manager对象。构造函数一离开,第二个管理器对象的引用计数就减少到0——删除了我刚刚创建完的对象。因此,CCD_ 11不应该从CCD_ 12本身创建。这是一个致命的缺陷 - 由于在双向关联中,对象需要传递一个指向自身的指针,C++库包含一个类
enable_shared_from_this
,它提供了一些模板魔法,可以在后台根据该类生成shared_ptr
。遗憾的是shared_from_this()
在构造函数中不可用,因为对象构建尚未完成。结论是,一个对象不能将引用传递给构造函数中的另一个对象。根本没有办法让它发挥作用解决方法是在构造函数之后直接调用setHead()
来自Java,在Java中,从构造函数传递this
是常态,这是你必须接受的
除了John Zwinck把我推向正确的方向之外,我发现David Kieras的一篇论文非常有用:http://www.umich.edu/~eecs381/讲义/C++11_smart_ptrs.pdf