堆分配对象的设置器

时间:2018-12-20 12:04:53

标签: c++ memory memory-management heap getter-setter

建议存在一个由堆中分配的对象组成的类。

class A
{
public:
    A() { m_b = new B; m_c = new C; }
    virtual ~A() { delete m_b; delete m_c; }

    inline B* b() const { return m_b; }
    inline C* c() const { return m_c; }

private:
    B* m_b;
    C* m_c;
}

哪种设置员最适合此类代码?

我想出了这个

inline void setB(B* b) const { delete m_b; m_b = b; }
inline void setC(C* c) const { delete m_c; m_c = c; }

但是有一个问题。如果我们添加了一个非堆变量,或者只是添加了一个我们不必使用此setter删除的变量,则它将在下一次调用setter之后被删除,这种情况将导致错误或意外行为。

我们也不能直接删除对象,因为获取器具有const修饰符。此外,这是不安全的,因为该类的用户可能不知道内部对象是否在堆中分配。

能否请您解释一下如何将setter与堆分配对象一起使用?

2 个答案:

答案 0 :(得分:4)

请勿使用原始的newdelete。改用智能指针-它们更安全,更易于使用。就您而言,std::unique_ptr看起来很不错:

class A
{
public:
    A() : m_b{std::make_unique<B>()}, m_c{std::make_unique<C>()} { }
    virtual ~A() = default;

    inline B* b() const { return m_b.get(); }
    inline C* c() const { return m_c.get(); }

private:
    std::unique_ptr<B> m_b;
    std::unique_ptr<C> m_c;
}

您的二传手可以简单地是:

void setB(std::unique_ptr<B>&& b) { m_b = std::move(b); }
void setC(std::unique_ptr<C>&& c) { m_c = std::move(c); }

答案 1 :(得分:1)

遵循规则0。

class A {
public:
  A()=default;
  virtual ~A() = default;

  B* b() const { return m_b.get(); }
  C* c() const { return m_c.get(); }

  void set_b(std::unique_ptr<B> in){m_b=std::move(in);}
  void set_c(std::unique_ptr<C> in){m_c=std::move(in);}
private:
  std::unique_ptr<B> m_b=std::make_unique<B>();
  std::unique_ptr<C> m_c=std::make_unique<C>();
};

独特的ptr既可以使所有权更清晰,也可以修复您不知道的错误,并可以减少代码的脆弱性。

固定的错误:

  1. 在您的原始代码中,如果抛出new C,您将泄漏B

  2. 如果您复制或移动原始对象,它们将被双重删除

如果要混合拥有和不拥有的子对象,则第一件事就是不要这样做。第二种是使用更高级的智能指针。但是类型应该具有明确的所有权语义,并且“也许是自己的也许不是”是代码的味道,并且会导致错误。