c++派生类与基类函数调用的问题

时间:2022-09-07 21:34:31
#include <iostream.h> 
class A  {
public:
    A(int n):num(n)    { Out( ); }
    A(const A& rhs):num(rhs.num)  
    {Out( );}
    void Out( ) {cout<<num<<endl; }
private:
    int num;
};
class B:public A  {
public:
    B(A& a) :obj(a),A(1) { }
    void Out( ) {  obj.Out( ); }
private:
    A  obj;
};
int main( ) {
    A a(8);                                                         
    B b1(a);                                                                
    B b2(b1);
    b2.Out();
    return 0;
}

输出为8 1 8 1 8 8
实在不理解最后3个数字

14 个解决方案

#1


A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8

#2


   A a(8);                    构造函数输出8                                      
    B b1(a);                        掉用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8                  
    B b2(b1);     没有显示的拷贝构造,调用b(a)进行转换,输出 1,8
    b2.Out(); 输出8
    return 0;

#3


引用 1 楼 adlay 的回复:
A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8

#4


引用 3 楼 qq_36751214 的回复:
Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数是会自动生成的.

c++派生类与基类函数调用的问题

#5


引用 4 楼 adlay 的回复:
Quote: 引用 3 楼 qq_36751214 的回复:

Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数是会自动生成的.


图片传错了, 重新来一下
c++派生类与基类函数调用的问题

#6


拷贝构造如果没有声明,类会自动生成。但是 该类中有B( a a)这种,通过类型转换来实现的靠背构造,编译器会优先选择,个人定义的。

#7


引用 3 楼 qq_36751214 的回复:
Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数可以自动生成, 隐式转型却不是可以自动生成的. 
如果被 B 类加上 B(const B& b) = delete; 禁止自动生成, 则只会参数编译错误, 不会自动去调用 B(A& a).


引用 6 楼 qq_36751214 的回复:
拷贝构造如果没有声明,类会自动生成。但是 该类中有B( a a)这种,通过类型转换来实现的靠背构造,编译器会优先选择,个人定义的。

编译器并不会优先选择需要转型的函数来调用的. 再 B(A& a) 中加点输出跟踪一下就知道了.

#8


adlay说得对。

#9


难道和编译环境有关系吗?本人这边使用的是ubuntu ,g++ 5.4.0 

B b(a);这个过程没有调用b的拷贝构造。

#10


 搞错了,抱歉。

#11


引用 2 楼 qq_36751214 的回复:
   A a(8);                    构造函数输出8                                      
    B b1(a);                        掉用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8                  
    B b2(b1);     没有显示的拷贝构造,调用b(a)进行转换,输出 1,8
    b2.Out(); 输出8
    return 0;


  B b1(a);                        调用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8        为什么先A(1)构造了临时对象了呢?我只知道先基类成员再继承类成员,成员中按照声明顺序进行初始化。从来没有见过这种无用的临时对象的呢

#12


很多啊,临时对象,就是栈空间的,如  A() ;就会生成没有名字的对象。

#13


当然了,本题中,并没有生成临时对象。因为拷贝构造传递的this是 b1的地址,所以,执行在执行  构造函数列表的时候,按照声明的顺序,优先构造num,继承的,然后构造、
成员对象。因此,1赋值给了b1,的基类num.

#14


问题很明了的,
第一个知识点:构造对象的顺序,先构造基类,在初始化子类成员
第二个知识点:默认拷贝构造函数调用

int main() {
A a(8);    //调用:A(int n)   a.num=8
B b1(a);   //B(A& a),先调用A(1)后 构造b1.obj  (基类不先构造如何构造子类)   b1.num=1, b1.obj.num= 8
B b2(b1);  //调用默认拷贝构造(浅拷贝),此时b2和b1值相同的,所有b2.num=1 ,b2.obj.num=8
b2.Out();
return 0;
}

#1


A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8

#2


   A a(8);                    构造函数输出8                                      
    B b1(a);                        掉用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8                  
    B b2(b1);     没有显示的拷贝构造,调用b(a)进行转换,输出 1,8
    b2.Out(); 输出8
    return 0;

#3


引用 1 楼 adlay 的回复:
A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8

#4


引用 3 楼 qq_36751214 的回复:
Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数是会自动生成的.

c++派生类与基类函数调用的问题

#5


引用 4 楼 adlay 的回复:
Quote: 引用 3 楼 qq_36751214 的回复:

Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数是会自动生成的.


图片传错了, 重新来一下
c++派生类与基类函数调用的问题

#6


拷贝构造如果没有声明,类会自动生成。但是 该类中有B( a a)这种,通过类型转换来实现的靠背构造,编译器会优先选择,个人定义的。

#7


引用 3 楼 qq_36751214 的回复:
Quote: 引用 1 楼 adlay 的回复:

A a(8)  ------> 8
B b1(a)  // B 中包含两个 A 对象, 一个是基类, 一个是成员, 先构造基类输出 1, 再构造成员输出 8  ------------> 1 8
B b2(b1) // b1 中基类 A 对象值为 1, 成员 A 对象值为 8   
               // 先执行基类的拷贝输出 1, 在执行成员的拷贝输出  8  -------------------> 1 8
b2.Out() // B 中定义的 Out 函数覆盖了基类 A 中的 Out 函数, 所以执行的是 B 的 Out, 输出成员 A 的值  8 -------------------> 8



这个是不正确的,b2(b1)没有执行B的默认拷贝构造,如果执行了的话,不会先执行基类的,如果执行基类的需要显示定义,调用基类的靠背构造,、
所有,实际走的是b(a),这个将基类的设置为1 ,然后执行输出8


拷贝构造函数可以自动生成, 隐式转型却不是可以自动生成的. 
如果被 B 类加上 B(const B& b) = delete; 禁止自动生成, 则只会参数编译错误, 不会自动去调用 B(A& a).


引用 6 楼 qq_36751214 的回复:
拷贝构造如果没有声明,类会自动生成。但是 该类中有B( a a)这种,通过类型转换来实现的靠背构造,编译器会优先选择,个人定义的。

编译器并不会优先选择需要转型的函数来调用的. 再 B(A& a) 中加点输出跟踪一下就知道了.

#8


adlay说得对。

#9


难道和编译环境有关系吗?本人这边使用的是ubuntu ,g++ 5.4.0 

B b(a);这个过程没有调用b的拷贝构造。

#10


 搞错了,抱歉。

#11


引用 2 楼 qq_36751214 的回复:
   A a(8);                    构造函数输出8                                      
    B b1(a);                        掉用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8                  
    B b2(b1);     没有显示的拷贝构造,调用b(a)进行转换,输出 1,8
    b2.Out(); 输出8
    return 0;


  B b1(a);                        调用B构造,先执行A(1)创建临时对象,输出1,后运行 obj(a)输出8        为什么先A(1)构造了临时对象了呢?我只知道先基类成员再继承类成员,成员中按照声明顺序进行初始化。从来没有见过这种无用的临时对象的呢

#12


很多啊,临时对象,就是栈空间的,如  A() ;就会生成没有名字的对象。

#13


当然了,本题中,并没有生成临时对象。因为拷贝构造传递的this是 b1的地址,所以,执行在执行  构造函数列表的时候,按照声明的顺序,优先构造num,继承的,然后构造、
成员对象。因此,1赋值给了b1,的基类num.

#14


问题很明了的,
第一个知识点:构造对象的顺序,先构造基类,在初始化子类成员
第二个知识点:默认拷贝构造函数调用

int main() {
A a(8);    //调用:A(int n)   a.num=8
B b1(a);   //B(A& a),先调用A(1)后 构造b1.obj  (基类不先构造如何构造子类)   b1.num=1, b1.obj.num= 8
B b2(b1);  //调用默认拷贝构造(浅拷贝),此时b2和b1值相同的,所有b2.num=1 ,b2.obj.num=8
b2.Out();
return 0;
}