c++为纯虚拟类创建默认的“构造函数/析构函数/复制构造函数/复制分配操作符”吗?

时间:2021-11-22 21:41:58

Do C++ compilers generate the default functions like Constructor/Destructor/Copy-Constructor... for this "class"?

c++编译器是否生成构造函数/析构函数/复制构造函数……对于这个“类”?

class IMyInterface
{
    virtual void MyInterfaceFunction() = 0;
}

I mean it is not possible to instantiate this "class", so i think no default functions are generated. Otherwise, people are saying you have to use a virtual destructor. Which means if i dont define the destructor virtual it will be default created, not virtual.

我的意思是不可能实例化这个“类”,所以我认为不会生成默认函数。否则,人们会说你必须使用虚拟析构函数。这意味着如果我不定义析构函数虚函数,它将被默认创建,而不是虚函数。

Furthermore i wannt to know if it is reasonable to define a virtual destructor for a pure virtual Interface, like the one above? (So no pointers or data is used in here, so nothing has to be destructed)

此外,我想知道为纯粹的虚拟接口定义一个虚拟析构函数是否合理,就像上面的那个那样?(这里不使用指针或数据,所以不需要销毁)

Thanks.

谢谢。

4 个解决方案

#1


1  

Furthermore i wannt to know if it is reasonable to define a virtual destructor for a pure virtual Interface, like the one above? (So no pointers or data is used in here, so nothing has to be destructed)

此外,我想知道为纯粹的虚拟接口定义一个虚拟析构函数是否合理,就像上面的那个那样?(这里不使用指针或数据,所以不需要销毁)

It's not only reasonable, it's recommended. This is because in the case of virtual function hierarchies, (automatically) calling a destructor of a specialized class also calls all destructors of it's base classes. If they are not defined, you should get a linking error.

它不仅是合理的,而且是推荐的。这是因为在虚函数层次结构的情况下,(自动地)调用专门类的析构函数也调用它的基类的所有析构函数。如果它们没有定义,您应该会得到一个链接错误。

If you define at least one virtual function in your class you should also define a virtual destructor.

如果在类中定义了至少一个虚函数,那么也应该定义一个虚析构函数。

The destructor can be defined with =default though:

析构函数可以定义为=default:

Here's a corrected (compilable) code example:

这里有一个修正的(可编译的)代码示例:

class ImyInterface
{
    virtual void myInterfaceFunction() = 0;
    virtual ~ImyInterface() = 0;
}

ImyInterface::~ImyInterface() = default;

#2


3  

Yes.

是的。

There is no wording that requires the class to be instantiable in order for these special member functions to be implicitly declared.

没有要求类实例化以使这些特殊的成员函数隐式声明的措辞。

This makes sense — just because you cannot instantiate the Base, doesn't mean a Derived class doesn't want to use these functions.

这是有意义的——仅仅因为您不能实例化基,并不意味着派生类不想使用这些函数。

struct Base
{
   virtual void foo() = 0;
   int x;
};

struct Derived : Base
{
   Derived() {};         // needs access to Base's trivial implicit ctor
   virtual void foo() {}
};

See:

看到的:

  • §12.1/5 (ctor)
  • §12.1/5(男星)
  • §12.8/9 (move)
  • §12.8/9(移动)
  • §12.8/20 (copy)
  • §12.8/20(复制)

#3


1  

Furthermore i wannt to know if it is reasonable to define a virtual destructor for a pure virtual Interface, like the one above? (So no pointers or data is used in here, so nothing has to be destructed)

此外,我想知道为纯粹的虚拟接口定义一个虚拟析构函数是否合理,就像上面的那个那样?(这里不使用指针或数据,所以不需要销毁)

Will the derived classes ever do anything in their destructors? Can you be certain they never will, even when somebody else takes over development?

派生类是否会在其析构函数中执行任何操作?你能确定他们永远不会,即使别人接管了发展吗?

The whole point of having a virtual destructor is not to make sure the base class is properly destructed, that will happen anyway. The point is that the derived class's destructor is called when you use a generic interface:

拥有一个虚拟的析构函数的目的不是为了确保基类被正确地破坏,这将会发生。重点是,当您使用通用接口时,将调用派生类的析构函数:

struct A {
  virtual ~A() {}
  virtual int f() = 0;
};

class B : public A {
  std::ifstream fh;
public:
  virtual ~B() {}
  virtual int f() { return 42; }
};

std::shared_ptr<A> a = new B;

When a goes out of scope, why is the ifstream closed? Because the destructor deletes the object using the virtual destructor.

当a超出范围时,为什么ifstream关闭?因为析构函数使用虚析构函数删除对象。

#4


0  

This addresses the second question about declaring a virtual destructor for an abstract base class (e.g. at least one member function is pure virtual). Here is a real world example of the LLVM clang++ compiler catching a potential problem. This occurred with the command line tools version supplied by Apple Developer for the Mac OS X Mavericks operating system.

这解决了关于为抽象基类声明虚拟析构函数的第二个问题(例如,至少有一个成员函数是纯虚拟的)。下面是一个实际的LLVM clang+编译器捕捉潜在问题的示例。这发生在苹果开发人员为Mac OS X Mavericks操作系统提供的命令行工具版本上。

Suppose you have a collection of derived classes that ultimately have the parent with the abstract base class to define the common interface. Then it is necessary to have a storage container like a vector that is intentionally declared to store a pointer to the abstract base class for each element. Later on, following good engineering practices, the container elements need to be "deleted" and the memory returned to the heap. The simplest way to do this is to traverse the vector element by element and invoke the delete operation on each one.

假设您有一组派生类,这些类最终具有抽象基类的父类来定义公共接口。然后需要有一个存储容器,比如一个向量,它被有意声明为为每个元素存储一个指向抽象基类的指针。稍后,遵循良好的工程实践,需要“删除”容器元素,并将内存返回到堆中。最简单的方法是逐个元素遍历vector元素并对每个元素调用delete操作。

Well, if the abstract base class does not declare the destructor as virtual, the clang++ compiler gives a friendly warning about calling the non-virtual destructor on an abstract class. Keep in mind that in reality only the derived classes are allocated from the heap with operator new. The derived class pointer type from the inheritance relationship is indeed the abstract base class type (e.g. the is-a relationship).

如果抽象基类没有将析构函数声明为虚函数,那么clang+编译器会友好地警告您在抽象类上调用非虚函数析构函数。请记住,实际上只有派生类是用操作符new从堆中分配的。继承关系中的派生类指针类型实际上是抽象基类类型(例如,isa关系)。

If the abstract base class destructor is not virtual, then how will the correct derived class' destructor be invoked to release the memory? At best the compiler knows better (at least potentially does with C++11), and makes it happen. If the -Wall compiler option is enabled, then at least the compilation warning should appear. However, at worse, the derived class destructor is never reached and the memory is never returned to the heap. Hence there is now a memory leak that may be very challenging to track down and fix. All it will take is a single addition of "virtual" to the abstract base class destructor declaration.

如果抽象基类析构函数不是虚函数,那么如何调用正确的派生类的析构函数来释放内存呢?编译器知道的最多(至少有可能使用c++ 11),并使之实现。如果启用了-Wall编译器选项,那么至少应该出现编译警告。然而,更糟糕的是,派生类析构函数不会被访问,内存也不会返回到堆中。因此,现在有一个内存泄漏,跟踪和修复可能非常困难。它只需要向抽象基类析构函数声明中添加一个“virtual”。

Example code:

示例代码:

class abstractBase
{
    public:
       abstractBase() { };
       ~abstractBase() { };

       virtual int foo() = 0;
};


class derived : abstractBase
{
    public:
        derived() { };
        ~derived() { };

        int foo() override { return 42; }
};

//
// Later on, within a file like main.cpp . . .
// (header file includes are assumed to be satisfied)
// 
vector<abstractBase*> v;

for (auto i = 0; i < 1000; i++)
{
    v.push_back(new derived());
}



//
// do other stuff, logic, what not
// 


// 
// heap is running low, release memory from vector v above 
//    
for (auto i = v.begin(); i < v.end(); i++)
{
    delete (*i); // problem is right here, how to find the derived class' destructor?
}

To resolve this potential memory leak, the abstract base class has to declare its destructor as virtual. Nothing else is required. The abstract base class now becomes:

为了解决这个潜在的内存泄漏,抽象基类必须声明它的析构函数为virtual。什么是必需的。抽象基类现在变成:

class abstractBase
{
    public:
       abstractBase() { };
       virtual ~abstractBase() { };   // insert virtual right here

       virtual int foo() = 0;
}

Note that the abstract base class has currently empty constructor and destructor bodies. As Lightness answered above, the compiler creates a default constructor, destructor, and copy constructor for an abstract base class (if not defined by the engineer). It is highly recommended to review any of The C++ Programming Language editions by the C++ creator, Bjarne Stroustrup, for more details on abstract base classes.

注意,抽象基类当前具有空的构造函数和析构体。如上所述,编译器为抽象基类创建一个默认构造函数、析构函数和复制构造函数(如果工程师没有定义的话)。强烈建议您阅读c++创建者Bjarne Stroustrup的c++编程语言版本,以了解更多关于抽象基类的详细信息。

#1


1  

Furthermore i wannt to know if it is reasonable to define a virtual destructor for a pure virtual Interface, like the one above? (So no pointers or data is used in here, so nothing has to be destructed)

此外,我想知道为纯粹的虚拟接口定义一个虚拟析构函数是否合理,就像上面的那个那样?(这里不使用指针或数据,所以不需要销毁)

It's not only reasonable, it's recommended. This is because in the case of virtual function hierarchies, (automatically) calling a destructor of a specialized class also calls all destructors of it's base classes. If they are not defined, you should get a linking error.

它不仅是合理的,而且是推荐的。这是因为在虚函数层次结构的情况下,(自动地)调用专门类的析构函数也调用它的基类的所有析构函数。如果它们没有定义,您应该会得到一个链接错误。

If you define at least one virtual function in your class you should also define a virtual destructor.

如果在类中定义了至少一个虚函数,那么也应该定义一个虚析构函数。

The destructor can be defined with =default though:

析构函数可以定义为=default:

Here's a corrected (compilable) code example:

这里有一个修正的(可编译的)代码示例:

class ImyInterface
{
    virtual void myInterfaceFunction() = 0;
    virtual ~ImyInterface() = 0;
}

ImyInterface::~ImyInterface() = default;

#2


3  

Yes.

是的。

There is no wording that requires the class to be instantiable in order for these special member functions to be implicitly declared.

没有要求类实例化以使这些特殊的成员函数隐式声明的措辞。

This makes sense — just because you cannot instantiate the Base, doesn't mean a Derived class doesn't want to use these functions.

这是有意义的——仅仅因为您不能实例化基,并不意味着派生类不想使用这些函数。

struct Base
{
   virtual void foo() = 0;
   int x;
};

struct Derived : Base
{
   Derived() {};         // needs access to Base's trivial implicit ctor
   virtual void foo() {}
};

See:

看到的:

  • §12.1/5 (ctor)
  • §12.1/5(男星)
  • §12.8/9 (move)
  • §12.8/9(移动)
  • §12.8/20 (copy)
  • §12.8/20(复制)

#3


1  

Furthermore i wannt to know if it is reasonable to define a virtual destructor for a pure virtual Interface, like the one above? (So no pointers or data is used in here, so nothing has to be destructed)

此外,我想知道为纯粹的虚拟接口定义一个虚拟析构函数是否合理,就像上面的那个那样?(这里不使用指针或数据,所以不需要销毁)

Will the derived classes ever do anything in their destructors? Can you be certain they never will, even when somebody else takes over development?

派生类是否会在其析构函数中执行任何操作?你能确定他们永远不会,即使别人接管了发展吗?

The whole point of having a virtual destructor is not to make sure the base class is properly destructed, that will happen anyway. The point is that the derived class's destructor is called when you use a generic interface:

拥有一个虚拟的析构函数的目的不是为了确保基类被正确地破坏,这将会发生。重点是,当您使用通用接口时,将调用派生类的析构函数:

struct A {
  virtual ~A() {}
  virtual int f() = 0;
};

class B : public A {
  std::ifstream fh;
public:
  virtual ~B() {}
  virtual int f() { return 42; }
};

std::shared_ptr<A> a = new B;

When a goes out of scope, why is the ifstream closed? Because the destructor deletes the object using the virtual destructor.

当a超出范围时,为什么ifstream关闭?因为析构函数使用虚析构函数删除对象。

#4


0  

This addresses the second question about declaring a virtual destructor for an abstract base class (e.g. at least one member function is pure virtual). Here is a real world example of the LLVM clang++ compiler catching a potential problem. This occurred with the command line tools version supplied by Apple Developer for the Mac OS X Mavericks operating system.

这解决了关于为抽象基类声明虚拟析构函数的第二个问题(例如,至少有一个成员函数是纯虚拟的)。下面是一个实际的LLVM clang+编译器捕捉潜在问题的示例。这发生在苹果开发人员为Mac OS X Mavericks操作系统提供的命令行工具版本上。

Suppose you have a collection of derived classes that ultimately have the parent with the abstract base class to define the common interface. Then it is necessary to have a storage container like a vector that is intentionally declared to store a pointer to the abstract base class for each element. Later on, following good engineering practices, the container elements need to be "deleted" and the memory returned to the heap. The simplest way to do this is to traverse the vector element by element and invoke the delete operation on each one.

假设您有一组派生类,这些类最终具有抽象基类的父类来定义公共接口。然后需要有一个存储容器,比如一个向量,它被有意声明为为每个元素存储一个指向抽象基类的指针。稍后,遵循良好的工程实践,需要“删除”容器元素,并将内存返回到堆中。最简单的方法是逐个元素遍历vector元素并对每个元素调用delete操作。

Well, if the abstract base class does not declare the destructor as virtual, the clang++ compiler gives a friendly warning about calling the non-virtual destructor on an abstract class. Keep in mind that in reality only the derived classes are allocated from the heap with operator new. The derived class pointer type from the inheritance relationship is indeed the abstract base class type (e.g. the is-a relationship).

如果抽象基类没有将析构函数声明为虚函数,那么clang+编译器会友好地警告您在抽象类上调用非虚函数析构函数。请记住,实际上只有派生类是用操作符new从堆中分配的。继承关系中的派生类指针类型实际上是抽象基类类型(例如,isa关系)。

If the abstract base class destructor is not virtual, then how will the correct derived class' destructor be invoked to release the memory? At best the compiler knows better (at least potentially does with C++11), and makes it happen. If the -Wall compiler option is enabled, then at least the compilation warning should appear. However, at worse, the derived class destructor is never reached and the memory is never returned to the heap. Hence there is now a memory leak that may be very challenging to track down and fix. All it will take is a single addition of "virtual" to the abstract base class destructor declaration.

如果抽象基类析构函数不是虚函数,那么如何调用正确的派生类的析构函数来释放内存呢?编译器知道的最多(至少有可能使用c++ 11),并使之实现。如果启用了-Wall编译器选项,那么至少应该出现编译警告。然而,更糟糕的是,派生类析构函数不会被访问,内存也不会返回到堆中。因此,现在有一个内存泄漏,跟踪和修复可能非常困难。它只需要向抽象基类析构函数声明中添加一个“virtual”。

Example code:

示例代码:

class abstractBase
{
    public:
       abstractBase() { };
       ~abstractBase() { };

       virtual int foo() = 0;
};


class derived : abstractBase
{
    public:
        derived() { };
        ~derived() { };

        int foo() override { return 42; }
};

//
// Later on, within a file like main.cpp . . .
// (header file includes are assumed to be satisfied)
// 
vector<abstractBase*> v;

for (auto i = 0; i < 1000; i++)
{
    v.push_back(new derived());
}



//
// do other stuff, logic, what not
// 


// 
// heap is running low, release memory from vector v above 
//    
for (auto i = v.begin(); i < v.end(); i++)
{
    delete (*i); // problem is right here, how to find the derived class' destructor?
}

To resolve this potential memory leak, the abstract base class has to declare its destructor as virtual. Nothing else is required. The abstract base class now becomes:

为了解决这个潜在的内存泄漏,抽象基类必须声明它的析构函数为virtual。什么是必需的。抽象基类现在变成:

class abstractBase
{
    public:
       abstractBase() { };
       virtual ~abstractBase() { };   // insert virtual right here

       virtual int foo() = 0;
}

Note that the abstract base class has currently empty constructor and destructor bodies. As Lightness answered above, the compiler creates a default constructor, destructor, and copy constructor for an abstract base class (if not defined by the engineer). It is highly recommended to review any of The C++ Programming Language editions by the C++ creator, Bjarne Stroustrup, for more details on abstract base classes.

注意,抽象基类当前具有空的构造函数和析构体。如上所述,编译器为抽象基类创建一个默认构造函数、析构函数和复制构造函数(如果工程师没有定义的话)。强烈建议您阅读c++创建者Bjarne Stroustrup的c++编程语言版本,以了解更多关于抽象基类的详细信息。