[英]Object returned from lambda loses property value
我正在學習幾年的課程,所以學習如何第一次使用序列化。
當“返回結果”在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;
}
Contact
不會使復制構造函數重載。 因此,生成默認值。
默認復制構造函數使用其默認構造函數復制所有(非static
)成員變量。 具體來說,指針的默認構造函數只是復制存儲的地址。
因此,使用Contact
的復制結構,構造了一個新的實例,其中Contact::address
指向與原始實例完全相同的Address
。
因此,改變jane
的地址也會改變joe
的地址。
這可能是有意或無意:
Contact
擁有其address
獨家所有權,則無意。 如果jane
和joe
沒有結婚,這可能是無意的。
在目前的狀態下,該設計還有另一個缺陷:
在銷毀Contact
時,哪個實例負責刪除address
指針對象?
如果將它添加到析構函數中~Contact()
事情開始變得更糟。 (delete一個jane
會刪除她的地址,並留下john
一個懸擺指針。)
就像現在一樣,破壞Contact
可能會導致內存泄漏。 (外部代碼必須負責刪除左側的Address
實例。這很難維護。)
這樣的設計問題並不罕見,並且引用了三條規則 :
如果其中之一
明確定義,然后另一個最明確。
使用C ++ 11(引入移動語義),這擴展到了Rule of Five添加
因此,一個明確的定義可能只是刪除它們:
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()
實例的Contact
有意義。
另一種可能的解決方案是將address
存儲為std::shared_ptr<Address>
而不是Address*
。 std::shared_ptr
(其中一個智能指針 )已經針對此類問題(涉及共享所有權)引入。
struct Contact {
std::shared_ptr<Address> address;
// default constructor with initialization to empty
Contact(): address(std::make_shared<Address>()) { }
// constructor with values
Contact(const Address &address):
address(std::make_shared<Address>(address))
{ }
// another constructor with values
Contact(const std::shared_ptr<Address> &address):
address(address)
{ }
// destructor.
~Contact() = default;
/* default destructor of shared_ptr is fully sufficient
* It deletes the pointee just if there are no other shared_ptr's to it.
*/
// copy constructor.
Contact(const Contact&) = default; // copies shared_ptr by default
// copy assignment.
Contact& operator=(const Contact&) = default; // copies shared_ptr by default
// move constructor.
Contact(Contact&&) = default;
// move assignment.
Contact& operator=(Contact&&) = default;
};
將“Five”設置為默認值在這種情況下實際上就像將它們排除在外一樣。
我在檢查時發現的鏈接不寫任何愚蠢的鏈接:
沒有令人信服的理由使用指向Contact
Address
指針,所以不要。 這也意味着編譯器生成的復制構造函數可以替換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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.