堆分配对象的设置器
Setters for heap-allocated objects
建议有一个 class 由堆中分配的对象组成。
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;
}
哪些 setter 最适合此类代码?
我想通了,
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 后被删除,这种情况会导致错误或意想不到的行为。
我们也不能直接删除对象,因为 getter 有 const
修饰符。此外,它也不安全,因为 class 的用户可能不知道内部对象是否分配在堆中。
能否请您解释一下如何将 setters 用于堆分配的对象?
不要使用原始 new
和 delete
。请改用智能指针 - 它们更安全且更易于使用。在你的情况下,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); }
遵循 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 既使所有权清晰,修复了您不知道的错误,又使您的代码不那么脆弱。
错误修复:
在您的原始代码中,如果 new C
抛出,您将泄漏 B
。
如果您复制或移动原始对象,它们会双重删除
如果你想混合拥有和不拥有的子对象,首先不要这样做。第二个是使用更高级的智能指针。但是类型应该有明确的所有权语义,"maybe own maybe not" 是代码味道,会导致错误。
建议有一个 class 由堆中分配的对象组成。
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;
}
哪些 setter 最适合此类代码?
我想通了,
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 后被删除,这种情况会导致错误或意想不到的行为。
我们也不能直接删除对象,因为 getter 有 const
修饰符。此外,它也不安全,因为 class 的用户可能不知道内部对象是否分配在堆中。
能否请您解释一下如何将 setters 用于堆分配的对象?
不要使用原始 new
和 delete
。请改用智能指针 - 它们更安全且更易于使用。在你的情况下,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); }
遵循 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 既使所有权清晰,修复了您不知道的错误,又使您的代码不那么脆弱。
错误修复:
在您的原始代码中,如果
new C
抛出,您将泄漏B
。如果您复制或移动原始对象,它们会双重删除
如果你想混合拥有和不拥有的子对象,首先不要这样做。第二个是使用更高级的智能指针。但是类型应该有明确的所有权语义,"maybe own maybe not" 是代码味道,会导致错误。