用非虚拟析构函数扩展基类是否很危险? [英] Is extending a base class with non-virtual destructor dangerous?

查看:93
本文介绍了用非虚拟析构函数扩展基类是否很危险?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下代码中:

class A {
};
class B : public A {
};
class C : public A {
   int x;
};

int main (int argc, char** argv) {
   A* b = new B();
   A* c = new C();

   //in both cases, only ~A() is called, not ~B() or ~C()
   delete b; //is this ok?
   delete c; //does this line leak memory?

   return 0;
}

当在具有成员函数的非虚拟析构函数的类上调用delete时(例如C类),内存分配器能否确定对象的正确大小?如果没有,内存泄漏了吗?

When calling delete on a class with a non-virtual destructor with member functions (like class C), can the memory allocator tell what the proper size of the object is? If not, is memory leaked?

第二,如果该类没有成员函数,也没有显式的析构函数行为(例如B类),那么一切都可以吗?

Secondly, if the class has no member functions, and no explicit destructor behaviour (like class B), is everything ok?

之所以这样问,是因为我想创建一个扩展std::string的类(不建议这样做,但是为了讨论起见,请耐心等待),并重载+=+操作员. -Weffc ++向我发出警告,因为std::string具有非虚拟析构函数,但是子类没有成员并且不需要在其析构函数中进行任何操作是否重要?

I ask this because I wanted to create a class to extend std::string, (which I know is not recommended, but for the sake of the discussion just bear with it), and overload the +=, + operator. -Weffc++ gives me a warning because std::string has a non virtual destructor, but does it matter if the sub-class has no members and does not need to do anything in its destructor?

仅供参考,+=重载是为了进行正确的文件路径格式化,因此可以使用以下路径类:

FYI the += overload was to do proper file path formatting, so the path class could be used like:

class path : public std::string {
    //... overload, +=, +
    //... add last_path_component, remove_path_component, ext, etc...
};

path foo = "/some/file/path";
foo = foo + "filename.txt";
std::string s = foo; //easy assignment to std::string
some_function_taking_std_string (foo); //easy implicit conversion
//and so on...

我只是想确保有人这样做:

I just wanted to make sure someone doing this:

path* foo = new path();
std::string* bar = foo;
delete bar;

不会对内存分配造成任何问题吗?

would not cause any problems with memory allocation?

推荐答案

所以每个人都说你做不到-这会导致不确定的行为.然而 在某些情况下它是安全的.如果您永远不会动态创建类的实例,那么您应该可以. (即没有新的通话)

So everyone has been saying you cant do it - it leads to undefined behaviour. However there are some cases where it is safe. If you are never creating instances of your class dynamically then you should be OK. (i.e. no new calls)

也就是说,这通常被认为是一件坏事,因为某人可能会在以后的某个时间尝试多态地创建一个. (您可以通过使用私有的未实现的运算符来防止这种情况的发生,但我不确定.)

That said, it is generally considered a bad thing to do as someone might try to create one polymorphically at some later date. ( You may be able to protect against this by having a private unimplemented operator new, but I'm not sure. )

我有两个例子,我不讨厌从具有非虚拟析构函数的类派生. 首先是使用临时变量创建语法糖...这是一个人为的示例.

I have two examples where I don't hate deriving from classes with non-virtual destructors. The first is creating syntactic sugar using temporaries ... here's a contrived example.

class MyList : public std::vector<int>
{
   public:
     MyList operator<<(int i) const
     {
       MyList retval(*this);
       retval.push_back(i);
       return retval;
     }
   private: 
     // Prevent heap allocation
     void * operator new   (size_t);
     void * operator new[] (size_t);
     void   operator delete   (void *);
     void   operator delete[] (void*);
};

void do_somthing_with_a_vec( std::vector<int> v );
void do_somthing_with_a_const_vec_ref( const std::vector<int> &v );

int main()
{
   // I think this slices correctly .. 
   // if it doesn't compile you might need to add a 
   // conversion operator to MyList
   std::vector<int> v = MyList()<<1<<2<<3<<4;

  // This will slice to a vector correctly.
   do_something_with_a_vec( MyList()<<1<<2<<3<<4 );

  // This will pass a const ref - which will be OK too.
   do_something_with_a_const_vec_ref( MyList()<<1<<2<<3<<4 );

  //This will not compile as MyList::operator new is private
  MyList * ptr = new MyList();
}

我能想到的另一种有效用法是C ++中缺少模板typedef.这是您可能会使用的方式.

The other valid usage I can think of comes from the lack of template typedefs in C++. Here's how you might use it.

// Assume this is in code we cant control
template<typename T1, typename T2 >
class ComplicatedClass
{
  ...
};

// Now in our code we want TrivialClass = ComplicatedClass<int,int>
// Normal typedef is OK
typedef ComplicatedClass<int,int> TrivialClass;

// Next we want to be able to do SimpleClass<T> = ComplicatedClass<T,T> 
// But this doesn't compile
template<typename T>
typedef CompilicatedClass<T,T> SimpleClass;

// So instead we can do this - 
// so long as it is not used polymorphically if 
// ComplicatedClass doesn't have a virtual destructor we are OK.
template<typename T>
class SimpleClass : public ComplicatedClass<T,T>
{
  // Need to add the constructors we want here :(
  // ...
   private: 
     // Prevent heap allocation
     void * operator new   (size_t);
     void * operator new[] (size_t);
     void   operator delete   (void *);
     void   operator delete[] (void*);
}

这里有一个更具体的例子.您想将std :: map与自定义分配器一起用于许多不同的类型,但是您不希望无法维护的

Heres a more concrete example of this. You want to use std::map with a custom allocator for many different types, but you dont want the unmaintainable

std::map<K,V, std::less<K>, MyAlloc<K,V> >

在您的代码中乱七八糟.

littered through your code.

template<typename K, typename V>
class CustomAllocMap : public std::map< K,V, std::less<K>, MyAlloc<K,V> >
{
  ...
   private: 
     // Prevent heap allocation
     void * operator new   (size_t);
     void * operator new[] (size_t);
     void   operator delete   (void *);
     void   operator delete[] (void*);
}; 

MyCustomAllocMap<K,V> map;

这篇关于用非虚拟析构函数扩展基类是否很危险?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆