virtual 关键字使派生对象无法访问方法

发布于 2024-12-10 02:24:26 字数 1574 浏览 0 评论 0原文

我有这样的代码:

#include <iostream>

class Super{
public:
    virtual void showName();
};

class Special1 : public Super {
public:
    void showName();
    void sayHello();
};

class Special2 : public Super {
public:
    void showName();
    void sayGoodbye();
};

void Super::showName() {
    std::cout << "I'm super!" << std::endl;
}

void Special1::showName() {
    std::cout << "I'm special1" << std::endl;
}

void Special1::sayHello() {
    std::cout << "Hello" << std::endl;
}

void Special2::showName() {
    std::cout << "I'm special2" << std::endl;
}

void Special2::sayGoodbye() {
    std::cout << "Goodbye" << std::endl;
}

int main () {
    Super *oSpec=new Super;

    Special1 *o1=static_cast<Special1 *>(oSpec);
    Special2 *o2=static_cast<Special2 *>(oSpec);

    oSpec->showName();
    o1->showName();
    o2->showName();

    o1->sayHello();
    o2->sayGoodbye();

    delete oSpec;

    return 0;
}

当我运行它时,它会显示以下输出:

I'm super!
I'm super!
I'm super!
Hello
Goodbye

但是,如果我从 Super 类的声明中删除 virtual 关键字:

class Super{
public:
    /*virtual*/ void showName();
};

输出将变得正确一:

I'm super!
I'm special1
I'm special2
Hello
Goodbye

那么,我的问题是,为什么virtual关键字的存在使得指针o1o2运行方法Super: :showName() 而不是Special1::showName() 还是 Special2::showName()

I have this code:

#include <iostream>

class Super{
public:
    virtual void showName();
};

class Special1 : public Super {
public:
    void showName();
    void sayHello();
};

class Special2 : public Super {
public:
    void showName();
    void sayGoodbye();
};

void Super::showName() {
    std::cout << "I'm super!" << std::endl;
}

void Special1::showName() {
    std::cout << "I'm special1" << std::endl;
}

void Special1::sayHello() {
    std::cout << "Hello" << std::endl;
}

void Special2::showName() {
    std::cout << "I'm special2" << std::endl;
}

void Special2::sayGoodbye() {
    std::cout << "Goodbye" << std::endl;
}

int main () {
    Super *oSpec=new Super;

    Special1 *o1=static_cast<Special1 *>(oSpec);
    Special2 *o2=static_cast<Special2 *>(oSpec);

    oSpec->showName();
    o1->showName();
    o2->showName();

    o1->sayHello();
    o2->sayGoodbye();

    delete oSpec;

    return 0;
}

When I run it, it shows this output:

I'm super!
I'm super!
I'm super!
Hello
Goodbye

But, if I remove the virtual keyword from the declaration of the Super class:

class Super{
public:
    /*virtual*/ void showName();
};

The output becomes the correct one:

I'm super!
I'm special1
I'm special2
Hello
Goodbye

Then, my question is, why the presence of the virtual keyword makes the pointers o1 and o2 run the method Super::showName() instead of Special1::showName() or Special2::showName()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

衣神在巴黎 2024-12-17 02:24:26

使用 virtual 时,输出更接近正确,而使用它时则不正确(除非您确实想要)。强制转换不会更改对象的类型,只会更改指针的类型。它们仍然是 Super 类型,因此应该运行 Super::showName

将一种指针类型转换为另一种指针类型不会更改所指向事物的类型。怎么可能呢?虚函数的全部要点是能够调用“通用”指针上的方法并获取正确的派生类方法。

为什么使用虚拟函数的典型例子是针对音乐家的。您可能有一个函数,通过在传递的每个 Musician * 上调用 Play 方法,使整个管弦乐队演奏。对于 Pianist 来说,必须调用 Pianist::Play

通常,编译器会在编译时确定要调用哪个函数——早期绑定。编译器确定知道的唯一信息是指针的类型。 virtual 关键字导致绑定在运行时后期发生,此时类成员的实际类型已知。

顺便说一句,您仍然可以通过使用范围覆盖来调用基类方法。例如o1->Super::showName();

事实上,你声称“正确”的结果是灾难性的。运行 Special1::showName() 时,this 指针指向非 Special1 类型(或派生自该类型)的对象:未定义的行为很容易导致崩溃。

The output is closer to correct with virtual and incorrect with it (unless you really wanted that). The cast doesn't change the type of the objects, just the type of the pointers. They are still of type Super, and so it's Super::showName that should run.

Casting one pointer type to another pointer type doesn't change the type of the thing pointed to. How could it? The whole point of virtual functions is to be able to call methods on 'generic' pointers and get the correct derived class method.

The classic example of why you use virtual functions is for musicians. You might have a function that causes the entire orchestra to play by calling the Play method on every Musician * it gets passed. For a Pianist, that has to call Pianist::Play.

Normally, the compiler figures out which function to call at compile time -- early binding. The only information the compiler is sure to know is the type of the pointer. The virtual keyword causes the binding to occur late, at run time, when the actual type of the class member is known.

By the way, you can still call the base class method, by using a scope override. For example o1->Super::showName();.

In fact, the result you claim is 'correct' is catastrophic. Running Special1::showName() with the this pointer pointing to an object that is not of type Special1 (or something derived from it) is undefined behavior and can easily lead to a crash.

稚然 2024-12-17 02:24:26

因为你的 main 中的代码是错误的。这些对象都说“我是超级”,因为对于它们来说(实际上,在后台),最派生的类型仍然是超级。这就是它们被创造出来的样子。

您的静态强制转换打破了所有规则,成为未定义的行为(任何事情都可能发生),因为您告诉编译器 o1Special1,而事实上,它是 < em>不是Special1的类型。

当您创建一堆派生对象并将它们存储在保存指向对象的指针的指针/容器中时,虚拟函数通常很有用。反之则不然。您想要创建 Special1Special2 类型的指针,并将它们存储在指向 super 的指针中。

Because your code in main is wrong. The objects all say "I'm super" because for them (in reality, in the background), the most derived type is still super. That's what they were created as.

Your static cast breaks all the rules, to be undefined behavior (anything can happen), because you tell the compiler that o1 is a Special1, when in fact, it is not a type of Special1.

virtual functions are usually useful when you create a bunch of derived objects, and store them in a pointer/container that holds pointers to the base object. Not the other way around. You want to create pointers of type Special1 and Special2 and store them in pointers to super.

梦里梦着梦中梦 2024-12-17 02:24:26

在这两种情况下,您的强制转换 (Special1 *o1=static_cast(oSpec);) 都是未定义的行为,因此就语言而言,任何输出都是可以接受的。您获取了一个 Super 类型的对象并对编译器撒谎,告诉它它实际上是一个派生类。在这种情况下,您仍然会得到父类 Super 的实际结果。

Your cast (Special1 *o1=static_cast<Special1 *>(oSpec);) is undefined behavior in both cases, so any output at all is acceptable as far as the language is concerned. You've taken an object of type Super and lied to the compiler, telling it that it's really a derived class. In this case, what happens is that you still get the result of the parent class Super that it really is.

请你别敷衍 2024-12-17 02:24:26

您的代码只是未定义。
试图解释它为什么以某种特定的方式工作是没有用的。

这里:

Super *oSpec=new Super;

oSpec 指向 Super 类型的对象。

因此在这些陈述中。强制转换具有未定义的行为,因为 oSpec 指向的对象既不是 Special 1 也不是 Special 2)。

Special1 *o1=static_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);

如果您使用dynamica_cast<> (就像你在创建类层次结构时应该做的那样)。你会发现结果是NULL。

Special1 *o1=dynamic_cast<Special1 *>(oSpec);
Special2 *o2=dynamic_cast<Special2 *>(oSpec);

std::cout << "O1(" << (void*)o1 << ") O2(" << (void*)o2 << ")\n";

这将表明两个指针都是 NULL。

Your code is just undefined.
Trying to reason why it works in a particular way is useless.

Here:

Super *oSpec=new Super;

oSpec points at an object of type Super.

Thus in these statements. The casts have undefined behavior, as the object pointed at by oSpec is neither a Special 1 or a Special 2).

Special1 *o1=static_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);

If you use dynamica_cast<> (like you should when casting up the class hierarchy). You will find the result are NULL.

Special1 *o1=dynamic_cast<Special1 *>(oSpec);
Special2 *o2=dynamic_cast<Special2 *>(oSpec);

std::cout << "O1(" << (void*)o1 << ") O2(" << (void*)o2 << ")\n";

This will show that both pointers are NULL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文