【问题标题】:How to prevent an object from being deleted via a pointer to its parent type?如何防止对象通过指向其父类型的指针被删除?
【发布时间】:2020-08-24 18:29:57
【问题描述】:
struct A
{};

struct B : A
{
    operator A() const = delete; // not work
};

int main()
{
    B* p_derived = new B();
    delete p_derived; // ok

    // How to make the following two lines illegal?
    A* p_base = new B();
    delete p_base; 
}

A的定义不能改变,其析构函数不是virtual;所以B的对象不应该被指向A的指针删除。

我不能使用struct B : protected A {};,因为我希望B 继承A 的所有public 成员并保持它们不变public

C++'s mixin patterns,这是一个常见的问题:

这没关系,除非通过指向原始类的指针多态地删除 mixin 类。

我的问题:

如何防止对象通过指向其父类型的指针被删除?

【问题讨论】:

  • 你打算如何删除B的实例?
  • 如果您无法更改A,您将无法实现这一目标。我会重新考虑这一点。也许B应该'has-a'A而不是'is-a'A,并删除你删除的类型转换运算符,以及一堆引用A字段的字段。
  • 在 C++ 的 mixin 模式中,这是一个常见的问题。 @user207421
  • 不,这实际上不是混合模式的常见问题,因为混合和多态删除在实践中很少一起出现。如果需要这样的东西,那么可以使 base/mixin 成为多态的,包括给它一个虚拟析构函数。一般来说,mixin 类并不打算以多态方式使用,因此它们没有虚拟析构函数,也不应该以多态方式删除。

标签: c++ oop design-patterns polymorphism virtual-destructor


【解决方案1】:

使 A 的析构函数受保护,然后子类可以访问它以正确销毁 B 类型的对象,但其他地方无法访问它。

struct A
{
protected:
   ~A() {}
};

struct B :  A
{
};

int main()
{
    A* p_base = new B();
    delete p_base; // This is now an error
}

如果你真的不能改变 A 那么你可以这样做:

struct A
{
};

struct AA : A
{
protected:
   ~AA() {}
};

struct B :  AA
{
};

int main()
{
    AA* p_base = new B();
    delete p_base; // How to make this a compile-time error?
}

如果您通过 A 而不是 AA 删除,这将不起作用。

根据您的问题的性质,您可以执行“#define A AA”或其他类似的丑陋黑客方法,以使其与现有代码库一起工作。

struct A
{
};

struct AA : A
{
protected:
   ~AA() {}
};

#define A AA
struct B :  A
{
};

int main()
{
    A* p_base = new B();
    delete p_base; // How to make this a compile-time error?
}

【讨论】:

  • 如果将 p_base 传递给接受实际 A* 作为参数的函数,然后将其删除,则最后一个宏 hack 将不起作用。
【解决方案2】:

简短的回答是你不能。

考虑这样的函数

// definition of struct A

void func(A *p)
{
    delete p;
}

和另一个编译单元中的调用者

// definitions of struct A and B as in question

void func(A *);

void caller()
{
     func(new B);
}

在这种情况下,func() 中的代码根本看不到B 类型,更不用说它接收到的指针p 实际指向B 的可能性。由于func() 执行delete p,函数caller() 导致func() 具有未定义的行为。

在这种情况下,真正的解决方案是 B 不应继承自 A。事实上,A 没有 virtual 析构函数,这已经强烈暗示了这一点。这将阻止caller() 使用B * 调用func()

如果你想让B提供与A相同的接口,那么你需要使用组合,并定义B的所有成员函数,以便它们转发到一个包含的A。例如;

 struct B
 {
      B() : a() {};    

      void some_member(some_type arg)
      {
          a.some_member(arg);      // assume A has this member that accepts these arguments
      }

      private:

         A a;
 };

如果struct A 可以修改,显然有可用的选项,但问题已经明确说明了一个不可能的用例。

【讨论】:

    猜你喜欢
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 2015-07-02
    • 1970-01-01
    • 2011-09-14
    • 2023-02-09
    • 2012-10-27
    • 2013-09-09
    相关资源
    最近更新 更多