首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对象返回的对象将丢失属性值。

对象返回的对象将丢失属性值。
EN

Stack Overflow用户
提问于 2018-09-28 00:34:47
回答 2查看 94关注 0票数 3

我正在上一门有几年历史的课程,所以第一次学习如何使用序列化。

当“返回结果”在lambda中执行时,联系人的Address属性将未初始化。注释掉的代码工作得很好,所以我非常肯定,我编译了Boost库,ok。

联系人上的名字还好。为什么地址没有?

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-09-28 06:22:59

Contact不会重载复制构造函数。因此,将生成缺省值。

默认复制构造函数使用其默认构造函数复制所有(非static)成员变量。具体来说,指针的默认构造函数只复制存储的地址。

因此,使用Contact的复制构造,将构造一个新实例,其中Contact::address指向与原始实例完全相同的Address

因此,改变jane的地址也会改变joe的地址。

这可能是有意的,也可能不是:

  • 有意分享资源
  • 无意中,如果Contact拥有其address的独家所有权。

如果janejoe没有结婚,这可能是意料之外的。

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

当一个address被销毁时,哪个实例负责删除Contact点?

如果将其添加到析构函数~Contact()中,情况就会变得更糟。(Deleteing jane会删除她的地址,并给john留下一个悬空指针。)

就像现在一样,破坏Contact可能会导致内存泄漏。(外部代码必须负责删除Address的左边实例。这是很难维持的。

这样的设计问题并不罕见,并导致了Rule of three,它说:

如果其中之一

  • 破坏者
  • 复制构造函数
  • 复制赋值算子

是显式定义的,那么另一个也很可能也定义了。

使用C++11 (引入移动语义),将其扩展到五个添加的规则

  • 移动构造函数
  • 移动赋值操作员。

因此,一个明确的定义可以是简单地删除它们:

代码语言:javascript
复制
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 (智能指针之一)。

代码语言:javascript
复制
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;
};

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

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

票数 1
EN

Stack Overflow用户

发布于 2018-09-28 09:10:13

没有令人信服的理由在Address中使用指向Contact的指针,所以不要使用,这也意味着编译器生成的复制构造函数可以替换clone

代码语言:javascript
复制
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;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52546765

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档