C ++:带有成员的抽象类中的纯虚析构函数

时间:2014-03-05 03:07:18

标签: c++ destructor pure-virtual virtual-destructor

我刚开始学习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;

我想知道Animal对象是否是动态创建然后销毁的,_age和_name成员是否会被正确销毁,因为Animal类的析构函数是空的?如果是这样,为什么?

谢谢:D

4 个答案:

答案 0 :(得分:4)

在您发布的示例中,您实际上并未正确销毁所有内容。在行

delete[] animalArray;

您正在删除Animal*的数组。请注意,这不会自动销毁指向的东西!您必须这样做:

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

这会破坏每个元素,然后会破坏容器。

现在,您的实际问题是询问私有成员变量是否会被彻底销毁。答案是肯定的 - 在你的析构函数运行之后,任何静态分配的变量&#39;析构函数也将由编译器调用。他们有义务自己清理。当您在示例中执行多态时,确实会调用(空)析构函数Animal::~Animal

请注意,这与上面的代码具有相同的警告:如果您改为

string* _name;

您在构造函数中动态分配(使用new),然后string*将被销毁,但指向 string将不会被销毁。因此,在这种情况下,您必须手动调用delete才能正确清理。

答案 1 :(得分:0)

它会,析构函数不会真正破坏你创建的对象,它会在被破坏的对象之前被调用,如果你在构造函数中没有新的东西,你就不需要删除它。

我试着指出一个样本来证明

当使用字符串(带有指针成员)对象作为成员变量时,它的析构函数将被调用,即使我们在类的析构函数中什么都不做

所以我尝试使用用户定义的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;
}

答案 2 :(得分:0)

是的,他们会的。通过将Animal析构函数设置为虚拟(或者在您的情况下为纯虚拟,与您的问题无关),确保在将Animal用作基类时,所有内容都已正确销毁。

Animal的析构函数将以反向初始化顺序为每个成员调用析构函数(即它将首先销毁_name并在之后销毁_age),从而确保所有内容都被正确释放。

答案 3 :(得分:0)

According to 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()'