C++ C++;和虚拟析构函数

C++ C++;和虚拟析构函数,c++,memory-management,virtual,new-operator,C++,Memory Management,Virtual,New Operator,我正在一个应用程序中编写一个实用程序类,在这个应用程序中,它们可能是未来的派生,也可能不是。我没有任何虚拟函数(使用虚拟DTOR的一般准则),因此考虑到内存限制,我选择在这个实用程序类中不使用虚拟析构函数 后来有几个程序员——有人从我的实用程序类中派生出来,将其添加到我的实用程序类中。现在,如果在代码中的任何地方,my new class in new'd and deleted正确的dtor将不会被调用,因为基类dtor不是虚拟的(参见示例代码) 除了返回并更改基类之外,在这种情况下还有什么解

我正在一个应用程序中编写一个实用程序类,在这个应用程序中,它们可能是未来的派生,也可能不是。我没有任何虚拟函数(使用虚拟DTOR的一般准则),因此考虑到内存限制,我选择在这个实用程序类中不使用虚拟析构函数

后来有几个程序员——有人从我的实用程序类中派生出来,将其添加到我的实用程序类中。现在,如果在代码中的任何地方,my new class in new'd and deleted正确的dtor将不会被调用,因为基类dtor不是虚拟的(参见示例代码)

除了返回并更改基类之外,在这种情况下还有什么解决方案

#include <iostream>
using namespace std;

class utility {
  int i, j;

  public:
    utility () { cout << "utility ctor\n";};
   ~utility () { cout << "utility dtor\n";};
    void dosomething () { cout << "haha\n";};
};

class addtoutility: public utility  {
  char *ch;

  public:
   addtoutility () { ch= new char(); cout << "added ctor\n";};
  ~addtoutility () { delete ch; cout << "added dtor\n";};
   void andnowaddsomefunctionality () {};
};

int main () {
  utility *u  = new addtoutility();
  //lots of interesthing code
  delete u;
}
#包括
使用名称空间std;
类效用{
int i,j;
公众:
实用工具(){cout
  • 通过正确类型的指针删除它。因为其他方法都不是虚拟的,所以没有必要对其进行多态处理
作品:

addtoutility *u  = new addtoutility();

delete u;
  • 虚拟化。如果你担心开销,那么它可能可以忽略不计
  • 使用CRTP使用静态继承。唯一的问题是,这可能会占用比vtable更多的内存

如果根本无法修改基类的代码,可以向派生类添加函数,如Destroy()。有点烦人,但如果你想到像Brush这样的Win32对象,我们总是这样做。

你的基类不是多态的,因此在任何情况下都没有指向基类的指针的价值。你的基类通过不在任何地方使用
virtual
关键字来记录它不是多态的事实

如果有人想将它用作多模态层次结构的基类,那么他们可以向从中派生的第一个类添加一个
virtual
析构函数,并在必要时保留指向该基类类型的指针

你不能阻止后来的程序员挖掘他们自己的陷阱,但是非polymorhphic类没有错,你不需要对它做任何事情来“保证它的安全”


为了证明这是一种常见且可接受的做法,您只需看一看标准库即可。有许多类(严格来说,大多数是类模板)没有虚拟函数,也没有虚拟析构函数。

事实上,应该由编写派生类的人来检查他们是否可以安全地使用基类,而不是相反。好的,thx!-我想确保情况是这样的(正如基类的编写者在其常见实践中不负责任一样,STL就是一个很好的例子!)Scott Meyers在有效C++中提出了类似的情况(第二ED)第14项,但没有提出解决方案,也没有站出来说这不是基类作家的责任——我猜这是隐含的;)如果知道为什么有人否决了这一点,那就好了。我是否误解了这个问题?我没有否决这个答案,但我承认我不理解它有什么帮助。也许你可以对它进行扩展?如果功能不正确的话在派生类中,这意味着您必须有一个指向派生类的指针才能使用它,但在这种情况下,直接的
delete
也是安全的。我猜下行投票者的观点是,必须强制转换到派生类,然后显式调用Destroy方法与必须强制转换到派生类并调用其析构函数。这种方法不会带来任何好处。好吧,看起来我误解了OP的要求。如果有人要从非虚拟基中派生,那么我们希望他们小心不要对其对象使用基类指针。我想知道为什么有人首先要从实用程序中派生……有没有任何se?有谁知道一个更好的例子,更接近真实代码吗?继承不是重用代码,而是供现有代码使用(即,您不应该派生以重用基类,而是使与您的基类一起工作的代码将与新类型一起工作)从这个角度来看,如果类没有任何虚拟函数,则不应派生它,这就是问题所在。确保通过正确的指针类型删除它的一种方法是使用
unique\u ptr
shared\u ptr
(当然,只有在真正需要时才动态分配它)。开销可能可以忽略不计,特别是当编译器发现没有从它继承任何内容时——不管它是否可以忽略是一个问题,但编译器不能基于没有派生类型的事实做出任何假设。虚拟函数的全部目的是,有人可以创建一个带有最终重写器的派生类编译器现在必须允许这种情况在future@DavidRodríguez Dribea甚至不是执行整个程序优化的多程编译器?(我知道编译器不会这样做,但有什么东西阻止它们吗?@Pubby:如果静态编译所有库,编译器将有机会(我想知道它是否真的被允许,更重要的是,编译器编写人员是否愿意在那里删掉一些字节)。如果编译到库中,编译器必须保留vptr,以防在不同的翻译单元中存在派生类型…我真的不希望在任何时候对此进行优化all@Pubby:快速思考之后,答案是尝试并执行该优化是疯狂的。类型的大小由编译器使用E无论创建该类型的实例,还是在不同的类中定义该类型的成员。如果编译器要删除vptr,它必须遍历该类型的所有用法并修复它们以匹配新的大小。对于不同类型的成员,这反过来意味着重新访问这些类型的所有用法…,以及所有这些用法uming表示该程序没有任何取决于这些大小的副作用。