提问者:小点点

什么时候我可以确定我可以从C++类继承?


假设我使用的是一个充满各种类的外部库。 什么时候我可以安全地继承那些类的遗产? 我知道基类必须有一个虚拟析构函数。 在使用类作为基类之前,还有什么我应该检查的吗? 我是否可以确定它是安全的,只有当医生这样说吗?


共2个答案

匿名用户

如果文档声明派生类型是安全的,请遵循文档。 如果由于某种原因,它的行为与文档不符,那么这是库的问题,是作者需要修复或提供解决方案的bug,因为他们没有承诺使用文档中保证的API。

任何不是final的类型都可以“安全”地派生; 更重要的是如何处理和销毁这种类型。 如果您从一个没有virtual析构函数的类型继承,那么这本身不会破坏任何内容; 它只是防止在将派生类型的析构函数从句柄销毁到基函数时调用该析构函数。

如果您只将类型从句柄销毁到派生类型(例如,您要么具体地保存它,要么从未将它从句柄销毁到基),那么这没有任何后果。

为了更好地解释我的观点,想象一下下面的层次结构:

class Base {
public:
    // No virtual destructor
    ...
};

class Derived : public Base {
public:
    ...
private:
    std::string m_something; // some leakable object
};

派生是完全安全的。 重要的是它是如何被摧毁的,因为是否会有问题。 为此,需要考虑两种不同的情况:自动情况和动态情况。

自动类型(“按值”类型)是安全的,无论它们是否具有静态生存期

auto d = Derived{ ... };
static auto sd = Derived{ ... };

在它们的生存期结束时,将调用析构函数derived::~derived,因为类型是具体已知的

动态对象不会自行被破坏。 它们的资源最终需要被清除,要么通过智能指针中的RAII自动清除,要么由调用delete的人清除,要么由显式调用~t()并释放内存。

如果它们被派生类型的句柄破坏,它们仍然是安全的,但如果它们被基句柄破坏,它们就不安全了。

auto* d1 = new Derived{ ... }; 
auto* d2 = new Derived{ ... };

// Deleting as a pointer to Base; ~Derived won't be called because ~Base is virtual
// This would be a memory leak
delete static_cast<Base*>(d1);  // bad 

// Deleting as a pointer to Derived -- ~Derived will be called, this is fine
delete d2; // good

在智能指针类型方面:

shared_ptr类型是安全的,因为它们总是从具体类型中破坏对象--即使它们被别名为基类。

void accept_base(std::shared_ptr<Base> b);

auto d = std::make_shared<Derived>(...);

// still safe
accept_base(std::move(d));

默认情况下,unique_ptr类型是不安全的,因为默认删除程序基于unique_ptrt类型进行删除。

例如:

auto d = std::make_unique<Derived>(...);

auto b = std::unique_ptr<Base>{std::move(d)};

// b will be destroyed at end of scope by calling ~Base, which is not virtual!

即使说了所有这些:如果您使用的库显式声明您将派生某个XYZ类,那么您仍然应该假定这就是类的使用方式。 此时,如果发生了不希望的事情,库维护人员将负责确保他们的代码按照文档执行,因为这是他们明确声明的API的一部分。

匿名用户

如果打算从基类引用或指针调用方法,还应该检查它们是否声明为虚拟。

除此之外,我将查看类的文档,以及它是否声明为final

实际上,不需要虚拟析构函数就可以安全地从类继承。 如果您想使用(从而破坏)类的基类指针,那么您只需要一个虚拟析构函数。

这完全取决于您打算如何使用派生类。

例如,如果您只想创建一个继承自给定类的类,但不打算在基类指针或引用中使用它。

Base baseObj;
Derived derivedObject; // This does not create any problems

如果您想从指针或引用(当然这也适用于智能指针)使用它到基类,如下所示:

Base* basePtr = new Base();
Base* basePtrToDerived = new Derived();
Derived* derivedPtrToDerived = new Derived();
// Do stuff here
delete basePtr;
delete basePtrToDerived; // if Base has no virtual destructor, only the destructor of Base is called
delete derivedPtrToDerived; // This will always call the destructor of Derived

你需要一个虚拟析构函数。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|类|继承)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?