【问题标题】:delete abstract non virtual dtor c++ [closed]删除抽象的非虚拟 dtor C++ [关闭]
【发布时间】:2020-10-15 21:22:07
【问题描述】:

我有一个代表抽象数据类型“Bag”的接口。为了实现这种抽象数据类型,我使用了基于数组和基于链接的实现。

这里是类的定义

编辑正如您所指出的,我已将虚拟析构函数添加到我的基类

template<class ItemType>
class BagInterface{

public:
    virtual int getCurrentSize() const = 0;
    
    virtual bool isEmpty() const = 0;
    
    virtual bool add(const ItemType& newEntry) = 0;
    
    virtual bool remove(const ItemType& anEntry) = 0;
    
    virtual int getFrequencyOf(const ItemType& anEntry) const = 0;
    
    virtual bool contains (const ItemType& anEntry) const = 0;

    virtual void clear() = 0;
    
    virtual vector<ItemType> toVector() const = 0;

    virtual ~BagInterface() = default;
};


#endif /* BagInterface_hpp */

我的 2 个派生类在他们的路上实现了这些方法。

我的基于链接的实现使用虚拟析构函数,因为与基于数组的实现不同,它动态分配内存,最终它必须使用“删除”关键字删除实例,以避免内存泄漏。

链接包的析构函数

template<class ItemType>
LinkedBag<ItemType>::~LinkedBag(){
    clear(); // Clears bag's content.
}

arraybag 的析构函数

template<class ItemType>
ArrayBag<ItemType>::~ArrayBag(){
    clear();
}

在所有实施问题之后,我想测试我的 ADT 操作。

我创建了一个函数,它以指针作为输入来表示袋子。

void bagTester(BagInterface<int>* bagPtr){
   // do some test }

在我的主要功能中,完成测试后,我想删除我的 bagPtr,因为我已经完成了它,所以是时候删除它了。

  int main(){
     BagInterface<int>* bagPtr = nullptr;
    
     char userChoice;
     cin>> userChoice;
    
     if(userChoice == 'A'){
     bagPtr = new ArrayBag<int>(); // Array based implementation 
    }else if(userChoice == 'L'){
     bagPtr = new LinkedBag<int>(); // Link bases implementation
    }

    bagTester(bagPtr); // test my bag
    
    delete bagPtr; // and now i'm finished with test let's delete the pointer
    bagPtr = nullptr; // deallocate
}

**此时我的错误正在发生,编译器会发出警告 -> **

In file included from main.cpp:2:
In file included from ./LinkedBag.hpp:5:
./BagInterface.hpp:36:31: warning: defaulted function definitions are a C++11 extension [-Wc++11-extensions]
    virtual ~BagInterface() = default;
                              ^
In file included from main.cpp:4:
./ArrayBag.hpp:28:5: error: exception specification of overriding function is more lax than base version
    ~ArrayBag();
    ^
main.cpp:48:22: note: in instantiation of template class 'ArrayBag<int>' requested here
        bagPtr = new ArrayBag<int>();
                     ^
./BagInterface.hpp:36:13: note: overridden virtual function is here
    virtual ~BagInterface() = default;
            ^
In file included from main.cpp:2:
./LinkedBag.hpp:25:1: error: exception specification of overriding function is more lax than base version
~LinkedBag();
^
main.cpp:52:22: note: in instantiation of template class 'LinkedBag<int>' requested here
        bagPtr = new LinkedBag<int>();
                     ^
./BagInterface.hpp:36:13: note: overridden virtual function is here
    virtual ~BagInterface() = default;
            ^
1 warning and 2 errors generated.

我检查了这个相关主题 -> Suppress delete-non-virtual-dtor warning when using a protected non-virtual destructor

我尝试了其中一项建议,即:

准则 #4:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的。

并且还尝试向 BagInterface 添加虚拟析构函数,但没有成功。(编辑:我知道我应该添加虚拟析构函数:))

我不明白这个问题,因为两个派生类都有自己的析构函数,arrayBag 有默认析构函数,linkedBag 有虚拟析构函数。

那么,我该如何避免警告呢?

【问题讨论】:

  • "尝试向 BagInterface 添加虚拟析构函数,但没有成功" 让我们以此为起点。您将虚拟析构函数添加到 BagInterface 是因为没有虚拟析构函数是错误的。所以在你添加虚拟析构函数之前的一切都是错误的。现在你有了一个虚拟析构函数,描述你的问题。
  • 您意识到现在您会泄漏内存,因为基于链接的实现的析构函数没有通过您的delete bagPtr 调用?
  • 让我们再试一次。没有虚拟析构函数是错误的。拥有一个虚拟析构函数是正确的。然而,单独拥有一个虚拟析构函数并不能保证正确性。您的代码可能还有其他问题。但是,您不会分享您的错误,因此我们无法判断这些错误是什么。如果您想回答您的问题,您应该实际发布编译器打印的实际错误。 “它没有用”不是有用的信息。我们可以猜测它没有工作。否则你可能不会在这里。
  • 明确一点:如果程序通过指向基类的指针删除派生类型的对象,而基类没有虚析构函数,则程序的行为是未定义的。
  • 在编程中你会发现一件事是有时修正一个错误会揭示更多错误。不要假设编译的代码比未编译的代码更接近正确。编写可编译且逻辑不正确的代码非常容易。此外,有时您可以添加一个非常需要的分号或右大括号,然后看到错误列表从一个错误增加到数百个,因为编译器现在可以解释更多代码并对其正确性进行注释。做正确的事,然后将发现的任何“新”错误排除在外。

标签: c++ pointers abstract-class abstract-data-type


【解决方案1】:

现在,当你运行这段代码时:

delete bagPtr;

不调用基于链接的实现的析构函数。

template<class ItemType>
class BagInterface{

public:
.....
virtual ~BagInterface() = default;
};

如果要通过指向 BagInterface 的指针删除从 BagInterface 派生的类(或者如果指向 BagInterface 派生类的 sharedpointer&lt;BagInterface&gt; 超出范围),则需要此析构函数。

我的基于链接的实现使用虚拟析构函数,因为 与基于数组的实现不同,它动态分配内存 最终它必须使用 'delete' 关键字来删除实例 避免内存泄漏。

这就是为什么您需要在基类中使用虚拟析构函数:如果没有,当您通过基类的指针删除派生类时,您会得到未定义的行为。

通过指向基址的指针删除对象会调用未定义的行为 除非基类中的析构函数是虚拟的。 source here

【讨论】:

  • 这比内存泄漏更糟糕。程序的行为未定义。
  • @PeteBecker 我不知道,谢谢。
  • @PeteBecker 非常感谢...
  • @user 非常感谢...
猜你喜欢
  • 2014-12-24
  • 1970-01-01
  • 2015-06-09
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 2016-03-23
相关资源
最近更新 更多