簡體   English   中英

從lambda返回的對象丟失屬性值

[英]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獨家所有權,則無意。

如果janejoe沒有結婚,這可能是無意的。

在目前的狀態下,該設計還有另一個缺陷:

在銷毀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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM