簡體   English   中英

C ++:帶有成員的抽象類中的純虛析構函數

[英]C++: Pure virtual destructor in abstract class with members

我剛開始學習C ++並偶然發現了這個問題。我用純虛析構函數編寫了這個抽象類:

#ifndef ANIMAL
#define ANIMAL
#include <string>
using namespace std;

class Animal {
public:
    Animal();
    virtual ~Animal() = 0;
    Animal(string name, int age);
    virtual string says() = 0;
    void setName(string name);
    void setAge(int age);
    string getName() const;
    int getAge() const;

private:
    int _age;
    string _name;
};
inline Animal::~Animal() { }

像這樣動態創建並銷毀......

Animal** animalArray = new  Animal*[10];
animalArray[0] = new Dog(name, age);
animalArray[1] = new Cat(name, age);
animalArray[2] = new Owl(name, age);

delete[] animalArray;

我想知道動物對象是否是動態創建然后銷毀的,_age和_name成員是否會被正確銷毀,因為Animal類的析構函數是空的? 如果是這樣,為什么?

感謝:D

在您發布的示例中,您實際上並未正確銷毀所有內容。 在線

delete[] animalArray;

你正在刪除一個Animal*的數組。 請注意, 這不會自動銷毀指向的東西! 你必須這樣做:

for(int i = 0; i < 3; ++i)
    delete animalArray[i];
delete[] animalArray;

這會破壞每個元素, 然后破壞容器。

現在,您的實際問題是詢問私有成員變量是否會被徹底銷毀。 答案是肯定的 - 在析構函數運行之后,編譯器也會調用任何靜態分配的變量的析構函數。 他們有義務自己清理。 當你在你的例子中做多態時,確實會調用(空)析構函數Animal::~Animal

請注意,這與上面的代碼具有相同的警告:如果你改為

string* _name;

你在構造函數中動態分配(使用new ),然后string*將被銷毀,但指向的 string將不會被銷毀。 因此,在這種情況下,您必須手動調用delete才能正確清理。

它會,析構函數不會真正破壞你創建的對象,它會在被破壞的對象之前被調用,如果你在構造函數中沒有新的東西,就沒有必要刪除它。

我試着指出一個樣本來證明

當使用字符串(帶有指針成員)對象作為成員變量時,它的析構函數將被調用,即使我們在類的析構函數中什么都不做

所以我嘗試使用用戶定義的String作為對象,因此我們很容易在析構函數中編寫一些日志。

它輸出:

constructor is called
constructor is called
constructor is called
operator constructor is called
destructor is called
operator constructor is called
destructor is called
virtual ~Dog()
virtual ~Animal()
destructor is called

它顯示的是當調用virtual~Animal()時,調用Animal類中的字符串object'detructor。

我們可以將字符串對象更改為字符串*(在construtor中使用new),同時在析構函數中仍然無效,我們將看到字符串的析構函數未被調用

#include <iostream>
#include <string.h>

using namespace std;


class String{
public:
    String(const char *str = NULL);
    String(const String &str);
    ~String();
    String operator+(const String & str);
    String & operator=(const String &str);
    bool operator==(const String &str);
    int Length();
    friend ostream & operator<<(ostream &o,const String &str);
    String SubStr(int start, int end);
private:
    char * charArray;
};

String::String(const char *str)
{
    if(str == NULL){
        charArray=new char[1];
        charArray[0]='\0';
    }else{
        charArray=new char[strlen(str)+1];
        strcpy(charArray,str);
    }
    std::cout<< "constructor is called" << std::endl;
}
String::String(const String &str)
{
    std::cout<< "constructor is called" << std::endl;
    charArray = new char[strlen(str.charArray)+1];
    strcpy(charArray,str.charArray);
}
String::~String()
{
    std::cout<< "destructor is called" << std::endl;
    delete [] charArray;
}
String String::operator+(const String &str)
{
    String res;
    delete [] res.charArray;
    res.charArray = new char[strlen(charArray)+strlen(str.charArray)+1];
    strcpy(res.charArray,charArray);
    strcpy(res.charArray+strlen(charArray),str.charArray);
    return res;
}
String & String::operator=(const String &str)
{
    if(charArray == str.charArray)
        return *this;
    delete [] charArray;
    charArray = new char[strlen(str.charArray)+1];
    strcpy(charArray,str.charArray);
    std::cout<< "operator constructor is called" << std::endl;
    return *this;
}
bool String::operator==(const String &str)
{
    return strcmp(charArray,str.charArray) == 0;
}
int String::Length()
{
    return strlen(charArray);
}
ostream & operator<<(ostream &o, const String &str)
{
    o<<str.charArray;
    return o;
}

String String::SubStr(int start, int end)
{
    String res;
    delete [] res.charArray;
    res.charArray = new char[end-start+1];
    for(int i=0; i+start<end; i++){
        res.charArray[i]=charArray[start+i];
    }
    res.charArray[end-start] = '\0';
    return res;
}


class Animal {
public:
    Animal();
    virtual ~Animal()=0;
    Animal(String name, int age);

public:
    int _age;
    String _name;
};
Animal::~Animal(){
    std::cout << "Animal::~Animal()" << std::endl;
}
Animal::Animal(String name, int age)
{
    this->_name = name;
    this->_age = age;
}

class Dog :public Animal
{
public:
    virtual ~Dog() {
         std::cout << "virtual ~Dog()" << std::endl;
    };
    Dog(String name, int age):Animal(name,age)
    {
        this->_name = name;
        this->_age = age;
    }
};

int main(){
   Animal* p = new Dog( String("dog"),1);
   delete p;
   return 0;
}

是他們會。 通過將Animal析構函數設置為虛擬(或者在您的情況下為純虛擬,與您的問題無關),確保在使用Animal作為基類時,所有內容都已正確銷毀。

Animal的析構函數將以反向初始化順序為每個成員調用析構函數(即它將首先銷毀_name並在之后銷毀_age),從而確保一切都被正確釋放。

根據Herb Sutter的說法,你不能用純虛擬析構函數實例化一個類,除非它還有一個正文。 原因是任何派生類都需要在自己的析構函數完成后調用該析構函數。

我們可以使用至少一個編譯器來驗證這一點: http//ideone.com/KcwL8W

#include <string>

class Animal
{
    public:
    virtual ~Animal() = 0;

    std::string _name;
};

class Dog : public Animal
{
};

int main() {
    Animal* pet = new Dog;
    delete pet;
    return 0;
}

/home/abDVbj/cc8ghrZk.o: In function `Dog::~Dog()':
prog.cpp:(.text._ZN3DogD2Ev[_ZN3DogD5Ev]+0xb): undefined reference to `Animal::~Animal()'
/home/abDVbj/cc8ghrZk.o: In function `Dog::~Dog()':
prog.cpp:(.text._ZN3DogD0Ev[_ZN3DogD0Ev]+0x12): undefined reference to `Animal::~Animal()'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM