我正在学习一门已有多年历史的课程,因此是第一次学习如何使用序列化。
当在lambda中执行“返回结果”时,Contact的Address属性变得未初始化。注释掉的代码可以正常工作,因此,我相当确定可以编译Boost库。
联系人上的姓名可以重新显示。为什么没有地址?
#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <sstream>
using namespace std;
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
struct Address
{
public:
string street, city;
int suite;
Address() {};
Address(string street, string city, int suite)
: suite(suite),street(street),city(city){ }
friend ostream& operator<<(ostream& os, const Address& obj)
{
return os
<< "street: " << obj.street
<< " city: " << obj.city
<< " suite: " << obj.suite;
}
private:
friend class boost::serialization::access;
template<class Ar> void serialize(Ar& ar, const unsigned int version)
{
ar & street;
ar & city;
ar & suite;
}
};
struct Contact
{
string name;
Address* address;
friend ostream& operator<<(ostream& os, const Contact& obj)
{
return os
<< "name: " << obj.name
<< " address: " << *obj.address;
}
private:
friend class boost::serialization::access;
template<class Ar> void serialize(Ar& ar, const unsigned int version)
{
ar & name;
ar & address;
}
};
int main()
{
Contact john;
john.name = "John Doe";
john.address = new Address{ "123 East Dr", "London", 123 };
auto clone = [](Contact c)
{
ostringstream oss;
boost::archive::text_oarchive oa(oss);
oa << c;
string s = oss.str();
Contact result;
istringstream iss(s);
boost::archive::text_iarchive ia(iss);
ia >> result;
return result;
};
// This works fine
//ostringstream oss;
//boost::archive::text_oarchive oa(oss);
//oa << john;
//string s = oss.str();
//Contact newJane;
//{
// istringstream iss(s);
// boost::archive::text_iarchive ia(iss);
// ia >> newJane;
//}
//newJane.name = "Jane";
//newJane.address->street = "123B West Dr";
//cout << john << endl << newJane << endl;
Contact jane = clone(john);
jane.name = "Jane";
jane.address->street = "123B West Dr";
cout << john << endl << jane << endl;
getchar();
return 0;
}
答案 0 :(得分:1)
Contact
不会使副本构造函数过载。因此,将生成默认值。
默认副本构造函数使用其默认构造函数复制所有(非static
)成员变量。具体来说,指针的默认构造函数只复制存储的地址。
因此,使用Contact
的副本构造,构造了一个新实例,其中Contact::address
指向与原始实例完全相同的Address
。
因此,更改jane
的地址也将更改joe
的地址。
这可能是有意的:
Contact
拥有其address
的专有所有权,则无意。如果jane
和joe
没有结婚,这可能是意外的。
在当前状态下,设计还有另一个缺陷:
address
被销毁时,哪个实例负责删除Contact
指针?
如果将其添加到析构函数~Contact()
中,情况将变得更糟。
(删除jane
会删除她的地址,而john
会留下一个悬空的指针。)
现在,销毁Contact
可能会导致内存泄漏。 (外部代码必须负责删除Address
的剩余实例。这很难维护。)
这种设计问题并不罕见,并导致Rule of three表示:
如果其中之一
是显式定义的,然后是另一个。
使用C ++ 11(引入了移动语义),它已扩展到五规则添加
因此,一个明确的定义可能就是删除它们:
struct Contact {
Address *address;
// default constructor with initialization to empty
Contact(): address(new Address()) { }
// constructor with values
Contact(const Address &address): address(new Address(address)) { }
// destructor.
~Contact()
{
delete address; // prevent memory leak
}
// move constructor.
Contact(Contact &&contact): address(contact.address)
{
contact.address = nullptr; // prevent two owners
}
// move assignment.
Contact& operator=(Contact &&contact)
{
address = contact.address;
contact.address = nullptr; // prevent two owners
return *this;
}
// prohibited:
Contact(const Contact&) = delete;
Contact& operator=(const Contact&) = delete;
};
这是有关内存管理的一项改进,但对于clone()
的{{1}}实例的意图却适得其反。
另一种可能的解决方案是将Contact
存储为address
而不是std::shared_ptr<Address>
。 std::shared_ptr
(智能指针之一)已针对此类问题(涉及共享所有权)引入。
Address*
在这种情况下,将“五个”设置为默认值实际上就像忽略它们一样。
我在检查时发现没有写任何愚蠢的链接:
答案 1 :(得分:1)
没有令人信服的理由在Address
中使用指向Contact
的指针,所以不要。这也意味着编译器生成的副本构造函数可以替换clone
。
struct Contact
{
string name;
Address address;
friend ostream& operator<<(ostream& os, const Contact& obj)
{
return os
<< "name: " << obj.name
<< " address: " << obj.address;
}
private:
friend class boost::serialization::access;
template<class Ar> void serialize(Ar& ar, const unsigned int version)
{
ar & name;
ar & address;
}
};
int main()
{
Contact john;
john.name = "John Doe";
john.address = Address{ "123 East Dr", "London", 123 };
Contact jane = john;
jane.name = "Jane";
jane.address.street = "123B West Dr";
cout << john << endl << jane << endl;
getchar();
return 0;
}