【问题标题】:Overriding of const member function in C++在 C++ 中重写 const 成员函数
【发布时间】:2021-09-11 08:11:00
【问题描述】:

考虑以下代码:

#include <iostream>
#include<string>
using namespace std;
 
class Base
{
public:
    virtual string print() const
    {
        return "This is Base class";
    }
};
 
class Derived : public Base
{
public:
    virtual string print() const
    {
        return "This is Derived class";
    }
};
 
void describe(Base p)
{
    cout << p.print() << endl;
}
 
int main()
{
    Base b;
    Derived d;
    describe(b);
    describe(d);
    return 0;
}

在执行此代码时,它给出的输出为

This is Base class
This is Base class

我对这个程序有几个疑问:

  1. Base类中的print函数是一个const成员函数,所以继承后,当我们试图覆盖Derived类中print的定义时,编译器为什么没有报错。

  2. 在这个程序中,我们如何将派生类对象 (d) 传递给需要数据类型 Base 参数的函数。

  3. 即使我们可以以某种方式将派生类对象传递给函数,为什么它会打印“这是基类”而不是“这是派生类”。

  4. 当我将派生类对象传递给函数 describe 时,是否会发生隐式类型转换?

如果有人能向我解释这段代码的工作原理,我将不胜感激。

【问题讨论】:

  • 这能回答你的问题吗? What is object slicing?
  • 当您 void describe(Base p) 执行此操作时,您的所有派生对象都将切片到其基类。
  • @Johan 这个链接确实解释了一些事情,但它仍然无法阐明我们如何成功地覆盖派生类中的 const 成员函数
  • const 函数并不意味着你不能覆盖它。它是函数签名的一部分。你可以在 constness 的基础上拥有多个同名的函数。
  • TL;DR: void describe(const Base&amp; p).

标签: c++ class oop inheritance


【解决方案1】:
  1. Base类中的print函数是一个const成员函数,所以继承后,当我们试图覆盖Derived类中print的定义时,编译器为什么没有报错。

没有错误,因为程序格式正确。不清楚为什么你会提到成员函数是 const 的细节。成员函数的常量对于函数是否可以被覆盖没有影响。您是否认为const 表示final

  1. 在这个程序中,我们如何将派生类对象 (d) 传递给需要数据类型 Base 参数的函数。

因为Base 类是Derived 类的公共基类,因此派生对象可以隐式转换为基类。转换的结果是基类子对象的副本。这就是俗称的“切片”。

  1. 即使我们可以通过某种方式将派生类对象传递给函数,为什么它会打印“这是基类”而不是“这是派生类”。

因为函数内部没有派生类对象。有一个基地的副本。

  1. 当我将派生类对象传递给函数 describe 时,是否会发生隐式类型转换?

是的。当您传递的参数类型与非引用参数的类型不同时,总是存在隐式转换。


间接对于运行时多态是必要的。使用对 base 的引用作为参数尝试相同的操作,您将看到调用的覆盖函数。

【讨论】:

    【解决方案2】:

    您的第一个问题是为什么它没有给您错误,因为您的函数是 const。 const 函数不会禁止您覆盖它。如果您希望您的函数不被派生类覆盖,您可以声明该函数final

    virtual string print() const final;
    

    现在这不会被任何其他派生类覆盖。至于您所关心的const。您可以在 constness 的基础上重载您的函数。例如,您可以拥有。

    virtual string print() const; // Does not modify anything
    virtual string print(); Can modify `this`
    

    对于第二个问题,可以将 Derived 类对象传递给期望 Base 类对象的函数。但只有通过引用或指针才有可能。

    void describe(const Base& p);
    void describe(Base* p);
    

    对于您通过副本传递的第三个问题,因此您的派生类对象被缩小到基类。这基本上会丢失除其基类之外的所有附加信息。

    第四题的答案与第三题相同。 这是您期望运行的功能齐全的代码。

        #include <iostream>
        #include<string>
        using namespace std;
     
        class Base
        { 
            public:
            virtual string print() const
            {
                return "This is Base class";
            }
        };
     
        class Derived : public Base
        {
        public:
            virtual string print() const override
            {
                return "This is Derived class";
            }
        };
     
        void describe(const Base& p)
        {
           cout << p.print() << endl;
        }
     
        int main()
        {
           Base b;
           Derived d;
           describe(b);
           describe(d);
           return 0;
        }
    

    在这里你可以看到我声明了覆盖函数override 来告诉编译器这个函数正在被它的基类覆盖。没有这个,它可以按预期工作,但它就像函数隐藏而不是覆盖。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-23
      • 2016-12-14
      • 2011-11-15
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 2021-01-08
      相关资源
      最近更新 更多