【问题标题】:why copy constructor and overloaded=operator of derived class not calling respective base class's copy constructor and overload=operator为什么派生类的复制构造函数和重载=操作符不调用各自基类的复制构造函数和重载=操作符
【发布时间】:2021-09-17 07:23:04
【问题描述】:

.h

#ifndef header
#define header

struct base
{
    private:
        int p,q;
    public:
        base();
        base(const base&);              
        base operator=(const base&);    
        ~base();
};

struct der: public base
{
    private:    
        int x,y;
    public:
        
        der();
        der(const der&);
        der operator=(const der&);
        ~der();
}; 
#endif

.cpp

#include"10.h"
#include<iostream>

base::base()
{
    std::cout<<"base ctor- 0 arg\n";
    p=0; q=0;
}
base::base(const base &b)
{
    std::cout<<"base copy ctor\n";
    p=b.p;
    q=b.q;
}
base base::operator=(const base &b)
{
    std::cout<<"base overloaded=operator\n";
    p=b.p;
    q=b.q;
    return *this;
}
base::~base()
{
    std::cout<<"base dctor\n";
}


der::der()
{
    std::cout<<"derived ctor- 0 arg\n";
}
der::der(const der &d)
{
    std::cout<<"derived copy ctor\n";
    x=d.x;
    y=d.y;
    
}
der der::operator=(const der &d)
{
    std::cout<<"derived overloaded operator\n";
    x=d.x;
    y=d.y;
    return *this;
    
}
der::~der()
{
    std::cout<<"derived dctor\n";
}

主要

#include"10.h"
#include<iostream>
int main()
{
    der d1,d2;
    std::cout<<"\n";
    
    d2=d1;
    std::cout<<"\n";
    
    der d3=d1;
    std::cout<<"\n";
}

输出

base ctor- 0 arg
derived ctor- 0 arg
base ctor- 0 arg
derived ctor- 0 arg

derived overloaded operator
base ctor- 0 arg
derived copy ctor
derived dctor
base dctor

base ctor- 0 arg
derived copy ctor

derived dctor
base dctor
derived dctor
base dctor
derived dctor
base dctor

1.当我没有创建任何基类对象时调用派生类的复制构造函数和重载=运算符时,基类的 0-arg 构造函数是如何调用的?

2.为什么派生类的重载=运算符不调用基类的相应重载=运算符。

3.与为什么派生类的复制构造函数不调用基类的相应复制构造函数类似。

【问题讨论】:

  • 当您覆盖一个函数并且该覆盖被执行时,只有该覆盖被执行。如果你想要“执行函数的基本版本,然后也执行这个额外的东西”的行为,你实际上必须写那个。该语言不会自动为您执行此操作。
  • -> der::der(const der &amp;d) : base(d) {/*..*/}.
  • @NathanPierson -- 你的观点是正确的:派生类中的函数不会自动调用它们对应的基类函数(构造函数和析构函数除外),但这里没有覆盖。
  • operator= 只是一个成员函数;无论你写什么,它都会做。如果你想调用基类版本,你必须自己调用它。派生类的构造函数将调用其基类的构造函数,但如果您不显式调用特定构造函数,编译器将生成调用 default 构造函数的代码。
  • @AbhishekMane - 这是真的。如果那是您想要的,请不要编写赋值运算符或复制构造函数。如果您选择自己编写,则必须告诉编译器您想要什么。如果你编写一个不调用基类构造函数的构造函数,编译器会生成调用默认构造函数的代码。这就是为什么它被称为“默认”。

标签: c++ inheritance copy-constructor


【解决方案1】:

让我们一一分析主程序中的3条语句。

我们将在最后讨论主程序中的第二条语句,因为这是需要最多解释的语句

语句 1:der d1, d2 这按预期工作,首先构造基类,然后为这两个对象构造派生类。

语句 3:der d3 = d1 在这里,我们尝试从另一个 der 类对象 d1 复制构造一个 der 类对象 d3。 但在构造派生类对象之前,首先需要创建基类对象。 基类中有2个构造函数,一个是默认构造函数,一个是复制构造函数,编译器将如何选择哪一个呢?

答案是我们需要告诉编译器选择哪个基类构造函数,告诉编译器的方法是使用派生类构造函数的初始化列表,并专门调用你想要的基类构造函数可以使用。

der::der(const der &d):base(d)
{
    std::cout<<"derived copy ctor\n";
    x=d.x;
    y=d.y;
}

在上面的代码中,我们指定了如何构造基类。 d 引用将向上转换为基类引用,以调用基类复制构造函数。

在您的原始版本中,编译器将需要基类的默认基类构造函数,而该构造函数正是被调用的,因为未指定如何构造基类。

语句 2:d2 = d1; 在此语句中,您调用的是赋值运算符,此处定义为

der der::operator=(const der &d)
{
    std::cout<<"derived overloaded operator\n";
    x=d.x;
    y=d.y;
    return *this;
}

重载的运算符只不过是函数,派生类函数不会自动调用基类函数。 如果您还需要调用该基类版本,那么您还需要在函数中添加调用的基类版本,如下所示

der der::operator=(const der &d)
{
    base::operator=(d);
    std::cout<<"derived overloaded operator\n";
    x=d.x;
    y=d.y;
    return *this;
}

一旦你做出这个改变,现在基类 operator= 也将被调用。 但是,如果您现在检查程序的输出,您会更加困惑。 该语句现在将产生的输出如下所示

base overloaded=operator
base copy ctor
base dctor
derived overloaded operator
base ctor- 0 arg
derived copy ctor
derived dctor
base dctor

为什么要调用这些构造函数和析构函数?

发生这种情况是因为 operator= 函数按值返回,这对于赋值运算符来说是不正确的。 在派生类运算符的修改版本中,我们将基类运算符称为 base::operator=(d); 该语句将调用下面的基类运算符函数

base base::operator=(const base &b)
{
    std::cout<<"base overloaded=operator\n";
    p=b.p;
    q=b.q;
    return *this;
}

但是,这个函数是按值返回的,这会导致基类的一个临时对象被创建,然后它也会被立即销毁。 在此之后,派生类运算符也按值返回,这将需要构造一个 der 类,而后者又需要构造一个基类,然后它也会立即被销毁。 导致我们看到的消息。

从 operator= 返回的正确方法是返回引用。 因此将基类和派生类运算符修改为如下 并在.h文件中进行相应的修改。

base& base::operator=(const base &b)
{
    std::cout<<"base overloaded=operator\n";
    p=b.p;
    q=b.q;
    return *this;
}

der& der::operator=(const der &d)
{
    base::operator=(d);
    std::cout<<"derived overloaded operator\n";
    x=d.x;
    y=d.y;
    return *this;
}

一旦你修改了上面的操作符函数,第二条语句将打印如下。

base overloaded=operator
derived overloaded operator

【讨论】:

  • 顺便说一句,如果我们没有明确地写这些东西,那么编译器生成的派生类的copy constructoroverloaded=operator分别调用基类的copy constructoroverloaded=operator,对吧?
  • 嗨 Abhishek Mane,这是正确的理解,编译器生成的函数也会发生这种情况。
猜你喜欢
  • 2013-06-23
  • 2020-07-02
  • 2015-09-27
  • 2019-08-07
  • 2012-02-28
  • 1970-01-01
  • 2014-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多