C++:如何防止对参数中构造的对象进行破坏?

Nic*_*ick 2 c++ inheritance pointers pass-by-reference

我在寻找这样的员工.

有一个A类,它有一个B类型的对象作为它的成员.由于我希望B成为其他类的基类,我需要使用指针或对象的引用,而不是它的副本,以正确使用A中的B的虚方法.但是当我写这样的代码

class B
  {public:
     B(int _i = 1): i(_i) {};
     ~B() 
       {i = 0; // just to indicate existence of problem: here maybe something more dangerous, like delete [] operator, as well! 
        cout << "B destructed!\n";
       };
     virtual int GetI () const  {return i;}; // for example
   protected:
     int i;
  };

class A
  {public:
      A(const B& _b): b(_b) {}
      void ShowI () {cout <<b.GetI()<<'\n';};
   private:
      const B& b;
  }; 
Run Code Online (Sandbox Code Playgroud)

并以这种方式使用它

B b(1);
A a(b);
a.ShowI();
Run Code Online (Sandbox Code Playgroud)

它完美地运作:

1
B destructed!
Run Code Online (Sandbox Code Playgroud)

A a(B(1));
a.ShowI();
Run Code Online (Sandbox Code Playgroud)

给出非常不需要的结果:对象b创建并且ab被设置为对它的引用,但是在A的构造函数完成后,对象b会破坏!输出是:

B destructed!
0
Run Code Online (Sandbox Code Playgroud)

我再次重申,在A中使用副本代替参考b

class A
  {public:
      A(B _b): b(_b) {}
      void ShowI () {cout <<b.GetI()<<'\n';};
   private:
      B b;
  }; 
Run Code Online (Sandbox Code Playgroud)

如果B是基类并且A调用它的虚函数,则不起作用.也许我太傻了,因为我不知道不知道正确的方法来编写必要的代码以使其完美运行(然后我很抱歉!)或者它可能根本不那么容易:-(

当然,如果B(1)被发送到函数或方法,而不是类构造函数,它就完美了.当然,我可以使用与此处描述的问题中相同的代码来创建B作为正确可克隆的基础或派生对象,但是对于这样容易看起来的问题,它看起来不是很难吗?如果我想使用无法编辑的B类怎么办?

Joh*_*itb 5

初始化a完成后,临时被破坏.并且引用成员将悬空,指的是不再存在的对象.

解决方案并不困难或复杂.多态对象通常不是临时的,但它存在的时间较长.因此,您只需使用智能指针来存储对传递对象的引用,并在此之前创建对象new.请注意,为此,该类B必须具有虚拟析构函数.shared_ptr当父对象结束生命期时,将关心破坏对象.复制时,shared_ptr将与副本共享指向的对象,并且都指向同一个对象.这OnwsPolymorphics对于许多用例的用户来说并不一定是显而易见的,因此您可能希望将其复制构造函数和赋值运算符设为私有:

struct OwnsPolymorphics {
  explicit OwnsPolymorphics(B *b):p(b) { }

private:
  // OwnsPolymorphics is not copyable. 
  OwnsPolymorphics(OwnsPolymorphics const&);
  OwnsPolymorphics &operator=(OwnsPolymorphics);

private:
  boost::shared_ptr<B> p;
};

OwnsPolymorphics owns(new DerivedFromB);
Run Code Online (Sandbox Code Playgroud)

或者您使用引用成员并将对象存储在堆栈上并将其传递给您正在显示的构造函数.


只有在直接分配给const引用时才能延长临时的生命周期:

B const& b = DerivedFromB(); 
Run Code Online (Sandbox Code Playgroud)

然后你也可以使用非虚拟析构函数,并可以调用虚函数b- 只要它们是const成员函数.这种特殊处理不是为类成员完成的,它的缺点是需要一个工作的复制构造函数(多态对象通常不可复制或设计成这样 - 它们不是通过值识别,而是通过标识来识别).任何临时性的东西都会在它出现的完整表达结束后被销毁 - 所以临时B对象就会发生.