为什么复制构造函数和 overloaded=派生类的运算符不调用相应基类的复制构造函数和 overload=operator

why copy constructor and overloaded=operator of derived class not calling respective base class's copy constructor and overload=operator

提问人:Abhishek Mane 提问时间:7/7/2021 更新时间:7/7/2021 访问量:428

问:

.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. 为什么 overloaded=派生类的运算符不调用基类的相应 overloaded=运算符。

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

C++ 继承 复制构造函数

评论

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

答:

1赞 Brajesh Agrawal 7/7/2021 #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 类,而 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

评论

1赞 Abhishek Mane 7/7/2021
顺便说一句,如果我们没有显式地编写这些东西,那么编译器就会生成和派生类调用,分别和基类,对吧?copy constructoroverloaded=operatorcopy constructoroverloaded=operator
1赞 Brajesh Agrawal 7/8/2021
嗨,Abhishek Mane,这是正确的理解,编译器生成的函数将发生这种情况。