为什么构造函数和析构函数中不要调用virtual函数
时间: 2024-03-18 08:45:35 浏览: 147
在构造函数和析构函数中调用 virtual 函数可能会导致意外的行为,因为在对象的生命周期的某些阶段,虚函数表(也称为 vtable)可能不会被完全初始化或已被部分或完全销毁。这可能会导致未定义的行为,甚至会导致程序崩溃。因此,一般来说,建议不要在构造函数和析构函数中调用 virtual 函数,以避免潜在的问题。
相关问题
定义一个document类,有数据成员name,从document派生出book类,增加数据成员pagecount。在构造函数和析构函数中输出信息,观察基类与派生类的构造函数和析构函数的调用顺序。
### 回答1:
定义一个document类,有数据成员name,表示文档名称。从document派生出book类,增加数据成员pagecount,表示书籍页数。在构造函数和析构函数中输出信息,观察基类与派生类的构造函数和析构函数的调用顺序。
代码如下:
```c++
#include <iostream>
#include <string>
using namespace std;
class document {
public:
document(const string& name) : name_(name) {
cout << "document constructor called: " << name_ << endl;
}
virtual ~document() {
cout << "document destructor called: " << name_ << endl;
}
protected:
string name_;
};
class book : public document {
public:
book(const string& name, int pagecount) : document(name), pagecount_(pagecount) {
cout << "book constructor called: " << name_ << ", " << pagecount_ << " pages" << endl;
}
~book() {
cout << "book destructor called: " << name_ << ", " << pagecount_ << " pages" << endl;
}
private:
int pagecount_;
};
int main() {
book b("The Lord of the Rings", 1178);
return ;
}
```
输出结果如下:
```
document constructor called: The Lord of the Rings
book constructor called: The Lord of the Rings, 1178 pages
book destructor called: The Lord of the Rings, 1178 pages
document destructor called: The Lord of the Rings
```
可以看到,先调用基类的构造函数,再调用派生类的构造函数;先调用派生类的析构函数,再调用基类的析构函数。
### 回答2:
对于这道题目,我们首先需要按照要求定义一个document类,并在其构造函数和析构函数中输出相应的信息。代码如下:
```c++
#include <iostream>
using namespace std;
class document {
public:
string name;
document(string n) : name(n) {
cout << "Constructing document " << name << endl;
}
~document() {
cout << "Destructing document " << name << endl;
}
};
```
接着,我们需要从document类派生出一个book类,并增加一个名为pagecount的数据成员。在构造函数和析构函数中,我们同样输出相应的信息。代码如下:
```c++
class book : public document {
public:
int pagecount;
book(string n, int pc) : document(n), pagecount(pc) {
cout << "Constructing book " << name << " with " << pagecount << " pages" << endl;
}
~book() {
cout << "Destructing book " << name << " with " << pagecount << " pages" << endl;
}
};
```
在上述代码中,我们使用了派生类构造函数的初始化列表来调用基类构造函数,并传递参数name。同时,我们还增加了pagecount参数,并将它输出到控制台上。
最后,为了观察基类与派生类的构造函数和析构函数的调用顺序,我们可以在主函数中创建一个book对象,然后在其作用域结束时销毁它。代码如下:
```c++
int main() {
book myBook("C++ Primer", 1000);
return 0;
}
```
运行程序后,我们可以得到以下输出结果:
```
Constructing document C++ Primer
Constructing book C++ Primer with 1000 pages
Destructing book C++ Primer with 1000 pages
Destructing document C++ Primer
```
从输出结果可以看出,在创建book对象时,先调用了document类的构造函数,然后调用了book类的构造函数。在销毁book对象时,先调用了book类的析构函数,然后调用了document类的析构函数。这表明在继承关系中,派生类的构造函数和析构函数会优先于基类的构造函数和析构函数执行。
### 回答3:
定义一个document类,可以包含下面的数据成员:
```cpp
class document {
protected:
std::string name;
public:
document(std::string _name): name(_name) {
std::cout << "Constructing document " << name << std::endl;
}
~document() {
std::cout << "Destructing document " << name << std::endl;
}
};
```
定义一个book类,从document类派生,增加数据成员pagecount:
```cpp
class book : public document {
protected:
int pagecount;
public:
book(std::string _name, int _pagecount): document(_name), pagecount(_pagecount) {
std::cout << "Constructing book " << name << " with " << pagecount << " pages" << std::endl;
}
~book() {
std::cout << "Destructing book " << name << std::endl;
}
};
```
上面的构造函数和析构函数中都输出了信息,方便我们观察构造函数和析构函数的调用顺序。
我们可以进行一些测试来观察基类与派生类的构造函数和析构函数的调用顺序:
```cpp
int main() {
document* doc1 = new document("Document 1");
std::cout << std::endl;
document* doc2 = new book("Book 1", 100);
std::cout << std::endl;
delete doc1;
std::cout << std::endl;
delete doc2;
std::cout << std::endl;
book book1("Book 2", 200);
std::cout << std::endl;
return 0;
}
```
我们首先创建一个document对象,然后创建一个book对象,将指向book对象的指针赋给一个document指针。接着我们删除doc1和doc2,最后创建一个book对象。
程序输出如下:
```
Constructing document Document 1
Constructing document Book 1
Constructing book Book 1 with 100 pages
Destructing document Document 1
Destructing book Book 1
Destructing document Book 1
Constructing document Book 2
Constructing book Book 2 with 200 pages
Destructing book Book 2
Destructing document Book 2
```
我们可以看到,创建document对象时只调用了document的构造函数,删除document对象时只调用了document的析构函数。
创建book对象时,首先调用document的构造函数,然后再调用book的构造函数。删除book对象时,首先调用book的析构函数,然后再调用document的析构函数。
在创建book1对象时,也是先调用document的构造函数,然后再调用book的构造函数。程序结束时,会先调用book的析构函数,然后再调用document的析构函数。
综上,我们可以看到,在派生类的构造函数和析构函数中,都会先调用基类的构造函数和析构函数。而在创建对象时,先调用基类的构造函数,再调用派生类的构造函数;在删除对象时,先调用派生类的析构函数,再调用基类的析构函数。
自定义一个类,并在其构造函数和析构函数中编写一段输出代码能表明该函数被调用,在主函数中定义一个类的对象,然后通过成员函数输出字符串“This is a C++ Program.”,在主程序中完成字符串输出功能,并测试构造函数和析构函数何时被调用。
在C++中,你可以创建一个类并自定义其构造函数和析构函数,以便在这些特殊时刻(对象创建和销毁)输出特定信息。首先,我们创建一个名为`MyClass`的类,包含一个构造函数`__construct()`和一个析构函数`~MyClass()`,这两个都是隐藏的(即不可见),但可以通过覆盖它们的方式来使用。
```cpp
class MyClass {
private:
std::string message;
public:
// 自定义构造函数
MyClass() : message{"Object created"} {
std::cout << "Constructor called with message: " << message << '\n';
}
// 自定义析构函数
virtual ~MyClass() {
std::cout << "Destructor called, message: " << message << '\n';
}
// 成员函数用于输出字符串
void printMessage() const {
std::cout << "This is a C++ Program.\n";
}
};
int main() {
// 创建MyClass的对象
MyClass myObject;
// 使用成员函数输出字符串
myObject.printMessage();
// 析构函数在离开作用域后自动调用
return 0;
}
```
当你运行这个程序时,将会看到以下输出:
1. "Constructor called with message: Object created"
2. "This is a C++ Program."
3. "Destructor called, message: Object created"
这表明了构造函数在创建对象时被调用,而析构函数在对象生命周期结束时(即离开`main`函数的范围后)被调用。
阅读全文