zoukankan      html  css  js  c++  java
  • C++解析(22):父子间的冲突

    0.目录

    1.同名覆盖

    2.赋值兼容

    3.函数重写遇上赋值兼容

    4.小结

    1.同名覆盖

    子类中是否可以定义父类中的同名成员?如果可以,如何区分?如果不可以,为什么?

    父子间的冲突:

    • 子类可以定义父类中的同名成员
    • 子类中的成员将隐藏父类中的同名成员
    • 父类中的同名成员依然存在于子类中
    • 通过作用域分辨符( :: )访问父类中的同名成员

    访问父类中的同名成员:

    示例:

    #include <iostream>
    
    using namespace std;
    
    class Parent
    {
    public:
        int mi;
        
        Parent()
        {
            cout << "Parent() : " << "&mi = " << &mi << endl;
        }
    };
    
    class Child : public Parent
    {
    public:
        int mi;
        
        Child()
        {
            cout << "Child() : " << "&mi = " << &mi << endl;
        }
    };
    
    int main()
    {
        Child c;
        
        c.mi = 100;
        
        c.Parent::mi = 1000;
        
        cout << "&c.mi = " << &c.mi << endl;
        cout << "c.mi = " << c.mi << endl;
        
        cout << "&c.Parent::mi = " << &c.Parent::mi << endl;
        cout << "c.Parent::mi = " << c.Parent::mi << endl;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Parent() : &mi = 0x7ffe251260c0
    Child() : &mi = 0x7ffe251260c4
    &c.mi = 0x7ffe251260c4
    c.mi = 100
    &c.Parent::mi = 0x7ffe251260c0
    c.Parent::mi = 1000
    

    类中的成员函数可以进行重载:

    1. 重载函数的本质为多个不同的函数
    2. 函数名参数列表是唯一的标识
    3. 函数重载必须发生在同一个作用域中

    子类中定义的函数是否能重载父类中的同名函数?

    示例:

    #include <iostream>
    
    using namespace std;
    
    class Parent
    {
    public:
        int mi;
        
        void add(int v)
        {
            mi += v;
        }
        
        void add(int a, int b)
        {
            mi += (a + b);
        }
    };
    
    class Child : public Parent
    {
    public:
        int mi;
        
        void add(int v)
        {
            mi += v;
        }
        
        void add(int a, int b)
        {
            mi += (a + b);
        }
        
        void add(int x, int y, int z)
        {
            mi += (x + y + z);
        }
    };
    
    int main()
    {
        Child c;
        
        c.mi = 100;    
        
        c.Parent::mi = 1000;
        
        cout << "c.mi = " << c.mi << endl;
        
        cout << "c.Parent::mi = " << c.Parent::mi << endl;
        
        c.add(1);
        c.add(2, 3);
        c.add(4, 5, 6);
        
        cout << "c.mi = " << c.mi << endl;
        
        cout << "c.Parent::mi = " << c.Parent::mi << endl;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    c.mi = 100
    c.Parent::mi = 1000
    c.mi = 121
    c.Parent::mi = 1000
    

    父子间的冲突:

    • 子类中的函数将隐藏父类的同名函数
    • 子类无法重载父类中的成员函数
    • 使用作用域分辨符访问父类中的同名函数
    • 子类可以定义父类中完全相同的成员函数

    示例:

    #include <iostream>
    
    using namespace std;
    
    class Parent
    {
    public:
        int mi;
        
        void add(int v)
        {
            mi += v;
        }
        
        void add(int a, int b)
        {
            mi += (a + b);
        }
    };
    
    class Child : public Parent
    {
    public:
        int mi;
        
        void add(int x, int y, int z)
        {
            mi += (x + y + z);
        }
    };
    
    int main()
    {
        Child c;
        
        c.mi = 100;    
        
        c.Parent::mi = 1000;
        
        cout << "c.mi = " << c.mi << endl;
        
        cout << "c.Parent::mi = " << c.Parent::mi << endl;
        
        c.Parent::add(1);
        c.Parent::add(2, 3);
        c.add(4, 5, 6);
        
        cout << "c.mi = " << c.mi << endl;
        
        cout << "c.Parent::mi = " << c.Parent::mi << endl;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    c.mi = 100
    c.Parent::mi = 1000
    c.mi = 115
    c.Parent::mi = 1006
    

    2.赋值兼容

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

    • 子类对象可以直接赋值给父类对象
    • 子类对象可以直接初始化父类对象
    • 父类指针可以直接指向子类对象
    • 父类引用可以直接引用子类对象

    示例——能编译通过的赋值兼容:

    #include <iostream>
    
    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; // 第1种兼容性
        
        Parent p1(c); // 第2种兼容性
        Parent& rp = c; // 第3种兼容性
        Parent* pp = &c; // 第4种兼容性
        
        rp.mi = 100;
        rp.add(5);      // 没有发生同名覆盖?
        rp.add(10, 10); // 没有发生同名覆盖?
        
        return 0;
    }
    

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

    • 子类对象退化为父类对象
    • 只能访问父类中定义的成员
    • 可以直接访问被子类覆盖的同名成员

    3.函数重写遇上赋值兼容

    特殊的同名函数:

    • 子类中可以重定义父类中已经存在的成员函数
    • 这种重定义发生在继承中,叫做函数重写
    • 函数重写是同名覆盖的一种特殊情况

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

    示例——函数重写遇上赋值兼容:

    #include <iostream>
    
    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->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;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    I'm Parent.
    I'm Parent.
    

    问题分析:

    • 编译期间,编译器只能根据指针的类型判断所指向的对象
    • 根据赋值兼容,编译器认为父类指针指向的是父类对象
    • 因此,编译结果只可能是调用父类中定义的同名函数


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

    4.小结

    • 子类可以定义父类中的同名成员
    • 子类中的成员将隐藏父类中的同名成员
    • 子类和父类中的函数不能构成重载关系
    • 子类可以定义父类中完全相同的成员函数
    • 使用作用域分辨符访问父类中的同名成员
    • 子类对象可以当作父类对象使用(赋值兼容)
    • 父类指针可以正确的指向子类对象
    • 父类引用可以正确的代表子类对象
    • 子类中可以重写父类中的成员函数
  • 相关阅读:
    面向对象的特征有哪些方面?
    构造器(constructor)是否可被重写(override)?
    swtich 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
    Java的运算符
    staitc关键字
    final关键字
    java的基本知识技能(二)
    java的基本操作和基本知识技能
    java概述------
    java中有几种方法实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10087617.html
Copyright © 2011-2022 走看看