C++解析(22):父子間的衝突
阿新 • • 發佈:2018-12-08
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; }
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
Parent() : &mi = 0x7ffe251260c0
Child() : &mi = 0x7ffe251260c4
&c.mi = 0x7ffe251260c4
c.mi = 100
&c.Parent::mi = 0x7ffe251260c0
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 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; }
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] 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;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] 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;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
I'm Parent.
I'm Parent.
問題分析:
- 編譯期間,編譯器只能根據指標的型別判斷所指向的物件
- 根據賦值相容,編譯器認為父類指標指向的是父類物件
- 因此,編譯結果只可能是呼叫父類中定義的同名函式
在編譯這個函式的時候,編譯器不可能知道指標p究竟指向了什麼。但是編譯器沒有理由報錯。於是,編譯器認為最安全的做法是呼叫父類的print函式,因為父類和子類肯定都有相同的print函式。
4.小結
- 子類可以定義父類中的同名成員
- 子類中的成員將隱藏父類中的同名成員
- 子類和父類中的函式不能構成過載關係
- 子類可以定義父類中完全相同的成員函式
- 使用作用域分辨符訪問父類中的同名成員
- 子類物件可以當作父類物件使用(賦值相容)
- 父類指標可以正確的指向子類物件
- 父類引用可以正確的代表子類物件
- 子類中可以重寫父類中的成員函式