添加字符串类型的类成员会导致调用基类函数而不是子函数

Adding a string-type class member causes base class function to be called instead of child

本文关键字:类函数 基类 调用 子函数 类型 字符串 成员 添加      更新时间:2023-10-16

为什么下面的代码打印0,但如果你注释掉"std::string my_string",它会打印1?

#include <stdio.h>
#include <iostream>
class A {
public:
virtual int foo() {
return 0;
}
private:
std::string my_string;
};
class B : public A {
public:
int foo() {
return 1;
}
};
int main()
{
A* a;
if (true) {
B b;
a = &b;
}
std::cout << a->foo() << std::endl;
return 0;
}

我也明白将 std::string 更改为 std:string* 也会导致代码打印 1,删除 if 语句也是如此,尽管我不明白为什么这是真的。

编辑:这似乎是由于悬空的指针。那么C++在 Java 中做这样的事情的标准模式是什么:

Animal animal; 
boolean isDog = false; 
// get user input to set isDog 
if (isDog) { 
animal = new Dog();
} else {
animal = new Cat();
}
animal.makeNoise(); // Should make a Dog/Cat noise depending on value of isDog.

问题

该程序具有未定义的行为。b仅在if体内的范围内。在访问悬空指针时,您不能指望逻辑结果。

int main()
{
A* a;
if (true) {
B b; // b is scoped by the body of the if.
a = &b;
} // b's dead, Jim.
std::cout << a->foo() << std::endl; // a points to the dead b, an invalid object
return 0;
}

TL;容灾解决方案

int main()
{
std::unique_ptr<A> a; // All hail the smart pointer overlords!
if (true) {
a = std::make_unique<B>();
}
std::cout << a->foo() << std::endl;
return 0;
} // a is destroyed here and takes the B with it. 

解释

您可以将a指向具有动态生存期的对象

int main()
{
A* a;
if (true) {
a = new B; // dynamic allocation 
} // b's dead, Jim.
std::cout << a->foo() << std::endl; 
delete a; // DaANGER! DANGER!
return 0;
}

不幸的是,delete a;也是未定义的行为A因为它具有非virtual析构函数。如果没有虚拟析构函数,a指向的对象将作为A被销毁,而不是作为B

解决方法是A提供一个虚拟析构函数,以允许它销毁正确的实例。

class A {
public:
virtual ~A() = default;
virtual int foo() {
return 0;
}
private:
std::string my_string;
};

没有必要修改B因为一旦一个函数被声明为virtual,它就会保持virtual它的子函数。请留意final

但最好避免原始动态分配,因此我们可以再进行一项改进:使用智能指针。

这让我们回到解决方案。

std::unique_ptr文档

std::make_unique文档