48-同名覆盖引发的问题

注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。

测试环境:Ubuntu 10.10

GCC版本:9.2.0

 

一、父子间的赋值兼容

1)子类对象可以当作父类对象使用(兼容性

    -    子类对象可以直接赋值给父类对象

    -    子类对象可以直接初始化父类对象

    -    父类指针可以直接指向子类对象(子类退化为父类)

    -    父类引用可以直接引用子类对象(子类退化为父类)

编程实验
子类对象的兼容性
48-1.cpp
#include <iostream>
#include <string>

using namespace std;

class Parent
{
public: 
    int mi;
    
    void add(int i)
    {
        mi += i;
        
        cout << "void add(int i) : mi = " << mi << endl;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
        
        cout << "void add(int a, int b) : mi = " << mi << endl;
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
        
        cout << "void add(int x, int y, int z) : mi = " << mi << endl;
    }
};

int main()
{
    Parent p;
    Child c;
    
    p = c;         //子类对象c,赋值给父类对象p,体现兼容性
    
    Parent p1(c);  //子类对象c初始化父类p1
    
    Parent& rp = c;      //父类引用子类,子类对象退化为父类
    Parent* pp = &c;    //父类指针指向子类,子类对退化为父类
    
    rp.mi = 100;    //
    rp.add(5);             // 没有发生同名覆盖,因为子类退化为父类
    rp.add(10, 10);        // 没有发生同名覆盖,因为子类退化为父类
    
    return 0;
}

操作:

1. g++ 48-1.cpp -o 48-1.out编译正确。打印结果:

void add(int i) : mi = 105
void add(int a, int b) : mi = 125

父类指针或者引用指向父类,调用同名函数时都显示的是父类中的

 

2. 修改代码:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public: 
    int mi;
    
    void add(int i)
    {
        mi += i;
        
        cout << "void add(int i) : mi = " << mi << endl;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
        
        cout << "void add(int a, int b) : mi = " << mi << endl;
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
        
        cout << "void add(int x, int y, int z) : mi = " << mi << endl;
    }
    
};

int main()
{
    Parent p;
    Child c;
    
    c.add(5);
    
    p = c;         //子类对象c,赋值给父类对象p,体现兼容性
    
    Parent p1(c);  //子类对象c初始化父类p1
    
    Parent& rp = c;      //父类引用子类,子类对象退化为父类
    Parent* pp = &c;    //父类指针指向子类,子类对退化为父类
    
    rp.mi = 100;    //
    rp.add(5);             // 没有发生同名覆盖,因为子类退化为父类
    rp.add(10, 10);        // 没有发生同名覆盖,因为子类退化为父类
    
    return 0;
}

g++ 48-1.cpp -o 48-1.out编译错误:

root@ubuntu:/home/gxn/DT/cpp/48# g++ 48-1.cpp -o 48-1.out
48-1.cpp: In function ‘int main()’:
48-1.cpp:44:9: error: no matching function for call to ‘Child::add(int)’
  c.add(5);
         ^
48-1.cpp:44:9: note: candidate is:
48-1.cpp:31:7: note: void Child::add(int, int, int)
  void add(int x, int y, int z)
       ^
错误:没有匹配的函数'Child::add(int)'
    c.add(5);
提示:候选函数是:
提示:void Child::add(int, int, int)
    void add(int x, int y, int z)

分析:

        子类访问继承自父类的同名函数,编译器提示找不到对应函数,继承时发生同名覆盖,找不到继承自父类的同名函数。

解决办法:作用域分辨符(::)

c.Parent::add(5);

3. 测试父类指针是否能访问子类,修改代码如下:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public: 
    int mi;
    
    void add(int i)
    {
        mi += i;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
    }
};

int main()
{
    Parent p;
    Child c;
    
    p = c;   //子类对象c,赋值给父类对象p,体现兼容性
    
    Parent p1(c);  //子类对象c初始化父类p1
    
    Parent& rp = c;   //父类引用子类,子类对象退化为父类
    Parent* pp = &c;    //父类指针指向子类,子类对退化为父类
    
    rp.mi = 100;    //
    rp.add(5);             // 没有发生同名覆盖,因为子类退化为父类
    rp.add(10, 10);        // 没有发生同名覆盖,因为子类退化为父类
    
    /* 为什么编译不过? */
    pp->mv = 1000;         //不能访问子类
    pp->add(1, 10, 100);   //不能访问子类
    
    return 0;
}

g++ 48-1.cpp -o 48-1.out编译错误:

48-1.cpp: In function ‘int main()’:
48-1.cpp:49:6: error: ‘class Parent’ has no member named ‘mv’
  pp->mv = 1000;
      ^
错误:'class Parent'没有成员'mv'      
48-1.cpp:50:20: error: no matching function for call to ‘Parent::add(int, int, int)’
  pp->add(1, 10, 100);
                    ^
错误:没有匹配的函数'Parent::add(int, int, int)'                        
48-1.cpp:50:20: note: candidates are:
48-1.cpp:11:7: note: void Parent::add(int)
  void add(int i)
       ^
48-1.cpp:11:7: note:   candidate expects 1 argument, 3 provided
48-1.cpp:16:7: note: void Parent::add(int, int)
  void add(int a, int b)
       ^
48-1.cpp:16:7: note:   candidate expects 2 arguments, 3 provided

分析:

        pp本意想访问子类成员,但是编译器不允许这种行为发生。综合之前打印结果:子类赋值给父类,只能通过父类引用对象或指针访问父类的成员。

 

2)当使用父类指针(引用)指向子类对象时

        -    子类对象退化为父类对象

        -    只能访问父类中定义的成员

        -    可以直接访问被子类覆盖的同名成员

 

二、特殊的同名函数

1)子类中可以重定义父类中已经存在的成员函数

2)这种重定义发生在继承中,叫做函数重写

3)函数重写是同名覆盖的一种特殊情况

父类和子类中都有print() ,这叫函数函数重写。

 

三、思考

                当函数重写遇上赋值兼容会发生什么?

编程实验
赋值兼容的问题
48-2.cpp
#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    int mi;
    
    void add(int i)
    {
        mi += i;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
    }
    
    void print()    //函数重写
    {
        cout << "I'm Parent." << endl;
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
    }
 
    void print()    //函数重写
    {
        cout << "I'm Child." << endl;
    }
};

void how_to_print(Parent* p)  //p指向子类,子类Child退化为父类Parent
{
    p->print();
}

int main()
{
    Parent p;
    Child c;
    
    c.print();   //I'm Child.
    p.print():   //I'm Parent.各自打印自己,两个print不冲突
    
    return 0;
}

操作:

1) g++ 48-2.cpp -o 48-2.out编译正确,打印结果:

I'm Child.
I'm Parent.    

分析:

        函数重写时,通过对象名都能访问各自函数!

 

2) 注释掉子类的print()函数,代码如下:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    int mi;
    
    void add(int i)
    {
        mi += i;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
    }
    
    void print()    //函数重写
    {
        cout << "I'm Parent." << endl;
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
    }
};

void how_to_print(Parent* p)  //p指向子类,子类Child退化为父类Parent
{
    p->print();
}

int main()
{
    Parent p;
    Child c;
    
    c.print();   
    p.print():  
    
    return 0;
}

g++ 48-2.cpp -o 48-2.out编译正确,打印结果:

I'm Parent.
I'm Parent.    

结论:注释掉子类同名函数,父类和子类访问的都是父类print()

如果注释父类的print函数,子类编译正常,父类访问错误,因为函数没有定义!

 

3) 修改代码:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    int mi;
    
    void add(int i)
    {
        mi += i;
    }
    
    void add(int a, int b)
    {
        mi += (a + b);
    }
    
    void print()    //函数重写
    {
        cout << "I'm Parent." << endl;
    }
};

class Child : public Parent
{
public:
    int mv;
    
    void add(int x, int y, int z)
    {
        mv += (x + y + z);
    }
    
    void print()    //函数重写
    {
        cout << "I'm Child." << endl;
    }
};

void how_to_print(Parent* p)  //p指向子类,子类Child退化为父类Parent
{
    p->print();
}

int main()
{
    Parent p;
    Child c;  

    how_to_print(&p);    // Expected to print: I'm Parent.
    how_to_print(&c);    // Expected to print: I'm Child.
    
    return 0;
}

g++ 48-2.cpp -o 48-2.out编译正确,打印结果:

I'm Parent.
I'm Parent.    

分析:

        期望父类p的print打印'I'm Parent.',子类c的print打印'I'm Child.'。但是都打印了'I,m Parent.'。

        这是为什么?如下分析。

 

四、父子间的赋值兼容(重点理解)

1)问题分析

        -    编译期间,编译器只能根据指针的类型判断所指向的对象

        -    根据赋值兼容,编译器认为父类指针指向的是父类对象

        -    因此,编译结果只可能是调用父类中定义的同名函数

void how_to_print(Parent* p)
{
    p->print();   //编译器这里解释为父类的print函数
} //子类虽然重定义了父类中同名函数,但是这里统一默认为父类的print函数!(重点理解)

       在编译这个函数的时候,编译器不可能知道指针p究竟指向了什么。但是编译器没有理由报错。于是,编译器认为最安全的做法是调用父类的print函数,因为父类和子类肯定有相同print函数。

 

五、问题

        编译器的处理方法是合理的吗是期望的吗

                合理的,不是我们期望的。

小结

1)子类对象可以当作父类对象使用(赋值兼容

2)父类指针可以正确的指向子类对象

3)父类引用可以正确的代表子类对象

4)子类中可以重写父类中的成员函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值