gpt4 book ai didi

c++ - 从 lambda 返回的对象丢失属性值

转载 作者:太空狗 更新时间:2023-10-29 21:33:09 26 4
gpt4 key购买 nike

我正在学习几年前的类(class),所以这是我第一次学习如何使用序列化。

当“返回结果”在 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拥有独家所有权.

如果janejoe未婚,这可能是无意的。

在目前的状态下,设计还有另一个缺陷:

哪个实例负责删除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() 的意图适得其反。 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;
};

在这种情况下,将“五”设置为默认值实际上与将它们排除在外一样。


我在检查不要写任何愚蠢的东西时发现的链接:

关于c++ - 从 lambda 返回的对象丢失属性值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52546765/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com