1. 程式人生 > >C++解析(22):父子間的沖突

C++解析(22):父子間的沖突

return 父類引用 本質 ostream spa 進行 mes 編譯期 ++

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.小結

  • 子類可以定義父類中的同名成員
  • 子類中的成員將隱藏父類中的同名成員
  • 子類和父類中的函數不能構成重載關系
  • 子類可以定義父類中完全相同的成員函數
  • 使用作用域分辨符訪問父類中的同名成員
  • 子類對象可以當作父類對象使用(賦值兼容)
  • 父類指針可以正確的指向子類對象
  • 父類引用可以正確的代表子類對象
  • 子類中可以重寫父類中的成員函數

C++解析(22):父子間的沖突