繼承
子類別繼承父類別
子類別繼承父類別,子類別可以使用父類別的成員變數與成員函式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
class Parent {
public:
string name_;
string tel_;
void setName(const string& name) {
name_ = name;
}
void setTel(const string& tel) {
tel_ = tel;
}
};
class Child:public Parent {
public:
int id_;
void setId(const int id) {
id_ = id;
}
void print() {
cout << "id = " << id_ << ", name = " << name_ << ", tel = " << tel_ << endl;
}
};
int main() {
// 建立child物件
Child child;
// 使用父類別的setName成員函式
child.setName("Cici");
// 使用父類別的setTel成員函式
child.setTel("08xxxxxx");
child.setId(100);
child.print();
return 0;
}
繼承方式
1
2
class Child1:public Parent {
};
以上的:public,public就是繼承方式
繼承方式有三種,public、protected、private。
若繼承方式是protected,父類別的public成員函式與成員變數會降級成protected,影嚮到孫子類別存取祖父類別的成員函式與變數。
若繼承方式是private,父類別的public成員函式與成員變數會降級成private,影嚮到孫子類別無法存取祖父類別的成員函式與變數。
繼承方式 | 在 main()存取父類別public成員函式 | 在子類別存取父類別public成員函式 | 在孫子類別存取祖父類別public成員函式 |
public | 可存取 | 可存取 | 可存取 |
protected | 無法存取 | 可存取 | 可存取 |
private | 無法存取 | 可存取 | 無法存取 |
以下程式碼透過public的printId()與setId()就可以由外部main()函式去寫入私有成員變數與讀取私有成員變數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
class Parent {
public:
string name_;
Parent(){}
void setId(const string& id) {
id_ = id;
}
void printId() {
cout << "id = " << id_ << endl;
}
private:
string id_;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
using namespace std;
class Parent {
public:
string name_;
Parent(){}
void setName(const string& name) {
name_ = name;
}
void setTel(const string& tel) {
tel_ = tel;
}
void setId(const string& id) {
id_ = id;
}
void printId() {
cout << "id = " << id_ << endl;
}
protected:
string tel_;
private:
string id_;
};
class Child1:public Parent {
public:
void print() {
// 父類別id_成員變數是private無法使用
cout << "name = " << name_ << ",tel_ = " << tel_ << endl;
}
};
//繼承方式為protected,父類別的public成員變數與函式全變成protected
class Child2:protected Parent {
public:
void print(const string& name, const string& tel, const string& id) {
// 父類別id_成員變數是private無法使用
// 父類別的成員函式降級protected,但仍能在子類別成員函式使用
// 無法在main()外部使用父類public的setName(),setTel(),printId()
setName(name);
setTel(tel);
setId(id);
printId();
// 父類別的成員變數的仍可給child成員函式使用
cout << "name = " << name_ << ",tel_ = " << tel_ << endl;
}
};
class GrandChild2:protected Child2 {
public:
void print(const string& name, const string& tel, const string& id) {
// 祖父id_成員變數是private無法使用
// 祖父的成員函式setName(),setTel(),printId()降級protected,但仍能在孫子類別成員函式使用
// 無法在main()外部使用祖父類public的setName(),setTel(),printId()
setName(name);
setTel(tel);
setId(id);
printId();
// 祖父類別的成員變數的仍可給child成員函式使用
cout << "name = " << name_ << ",tel_ = " << tel_ << endl;
}
};
class Child3:private Parent {
public:
void print(const string& name, const string& tel, const string& id) {
// 父類別id_成員變數是private無法使用
// 父類別的成員函式setName(),setTel(),printId()降級private, 但仍能在子類別成員函式使用,孫子類別無法使用
// 無法在main()外部使用父類public的setName(),setTel(),printId()
setName(name);
setTel(tel);
setId(id);
printId();
// 父類別的成員變數的仍可給child成員函式使用
cout << "name = " << name_ << ",tel_ = " << tel_ << endl;
}
};
class GrandChild3:private Child3 {
public:
void print(const string& name, const string& tel, const string& id) {
// 祖父類別id_成員變數是private無法使用
// 祖父類別的成員函式setName(),setTel(),setId(),printId()降級private無法使用
// 祖父類別的public方法setName(),setTel(),setId(),printId()全部降級成private無法使用
//setName(name);
//setTel(tel);
//setId(id);
//printId();
// 祖父類別的成員變數降級private無法使用
// cout << "name = " << name_ << ",tel_ = " << tel_ << endl;
}
};
int main() {
// 建立child1物件
// 因為繼承方式是public,所以父類別的public成員函式可以使用
Child1 child1;
child1.setName("Cici");
child1.setTel("080xxxx");
child1.print();
Child2 child2;
// 因為繼承方式是protected,所以父類別的public成員函式全降級變為protected成員函式
// protected就不能在main()外部使用父類別的public成員函式setName()與setTel()
//child2.setName("Cici2");
//child2.setTel("080xxxx2");
child2.print("Bill","09xxxx","0001");
GrandChild2 gchild2;
gchild2.print("gBill","g09xxxx","g0001");
Child3 child3;
child3.print("Jerry","08xxxx","0002");
GrandChild3 gchild3;
gchild3.print("gJerry","g08xxxx","g0002");
return 0;
}
name = Cici,tel_ = 080xxxx
id = 0001
name = Bill,tel_ = 09xxxx
id = g0001
name = gBill,tel_ = g09xxxx
id = 0002
name = Jerry,tel_ = 08xxxx
改變父類別存取權限
使用using 子類別可以改變父類別成員變數的權限,但無法修改父類別權限為private成員變數。
以下的例子public權限改為private,protected改為public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
class Parent {
public:
string name_;
protected:
string tel_;
private:
string id_;
};
class Child:public Parent {
public:
// 父類別別protected成員變數改成public
using Parent::tel_;
private:
// 父類別別public成員變數改成private
using Parent::name_;
};
int main() {
Child child;
child.tel_ = "087000";
return 0;
}
繼承的建構子與解構子
-
建立子類別物件,呼叫父類別建構子->呼叫子類別建構子
-
銷毀子類別物件,呼叫子類別解構子->呼叫父類別解構子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
class Parent {
public:
Parent() {cout << "Parent建構子" << endl;}
~Parent() {cout << "Parent解構子" << endl;}
};
class Child:public Parent {
public:
Child() {cout << "Child建構子" << endl;}
~Child() {cout << "Child解構子" << endl;}
};
int main() {
Child child;
return 0;
}
Parent建構子
Child建構子
Child解構子
Parent解構子
繼承的範圍運算子
以下的範例,父類別與子類別與孫類別都有print()成員函式,也都有name的成員變數,使用範圍運算子,可以明確指示出要使用祖父類別或父類別的成員函式或成員變數。
呼叫父類別成員變數語法
子類物件.父類別::成員變數
呼叫父類別成員函式語法
子類物件.父類別::成員函式()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
class Parent {
public:
string name_ = "Parent";
void print() {
cout << "Parent name:" << name_ << endl;
}
};
class Child:public Parent {
public:
string name_ = "Child";
void print() {
cout << "Child name:" << name_ << endl;
}
};
class GrandChild:public Child {
public:
string name_ = "GrandChild";
void print() {
cout << "GrandChild name:" << name_ << endl;
}
};
int main() {
GrandChild grandChild;
cout << "Parent name = " << grandChild.Parent::name_ << endl;
cout << "Child name = " << grandChild.Child::name_ << endl;
cout << "GrandChild name = " << grandChild.GrandChild::name_ << endl;
cout << "------------------------" << endl;
grandChild.Parent::print();
grandChild.Child::print();
grandChild.GrandChild::print();
return 0;
}
Parent name = Parent
Child name = Child
GrandChild name = GrandChild
------------------------
Parent name:Parent
Child name:Child
GrandChild name:GrandChild
也可以使用一層層呼叫方式,呼叫成員變數。呼叫方式子類別::父類別::父類別的成員變數
1
cout << "Parent name = " << grandChild.Child::Parent::name_ << endl;
父類別子類別大小
sizeof
以下的程式範例可以看出父類別有3個int變數(m1,m2,m3),int大小為4byte,所以父類別總共為12byte
子類別只有1個int變數(m4),4byte,但子類別會繼承父類別的變數,所以12byte + 4byte = 16byte,因此子類別建立的記憶體大小為16byte。
透過自建operator new()函式,可以看到建立Child的大小為16byte。
使用以下語法可以看出父類別的大小
sizeof(父類別)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;
void* operator new(size_t size) {
cout << "全域new size = " << size << endl;
void* ptr = malloc(size);
cout << "全域 address = " << ptr << endl;
return ptr;
}
void operator delete(void* ptr) {
if (ptr == nullptr) return;
cout << "全域 delete address = " << ptr << endl;
free(ptr);
}
class Parent {
public:
int m1_ = 10;
int m2_ = 20;
int m3_ = 30;
};
class Child:public Parent {
public:
int m4_ = 40;
};
int main() {
cout << "size of Parent = " << sizeof(Parent) << endl;
cout << "size of Child = " << sizeof(Child) << endl;
Child* ptr = new Child;
return 0;
}
size of Parent = 12
size of Child = 16
全域new size = 16
全域 address = 0x600000008030
記憶體布局 Memory layout
windows使用以下語法
cl 原始檔案 /d1 reportSingleClassLayout類別名
cl test.cpp /d1 reportSingleClassLayoutTestA
Parent
mac使用以下語法
-A 10 代表只印出10筆
clang++ -Xclang -fdump-record-layouts -c 原始檔案.cpp | grep -A 10 "class 類別名"
clang++ -Xclang -fdump-record-layouts -c test1.cpp | grep -A 10 "class Parent"
拿上一個範例的程式碼,顯示出來的記憶體布局如下:
0 | class Parent
0 | int m1_
4 | int m2_
8 | int m3_
| [sizeof=12, dsize=12, align=4,
| nvsize=12, nvalign=4]
- m1_從0byte開始
- m2_從4byte開始
- m3_從8byte開始
int整數大小是4byte,所以Parent類別的大小為12
Child
clang++ -Xclang -fdump-record-layouts -c test1.cpp | grep -A 10 "class Child"
0 | class Child
0 | class Parent (base)
0 | int m1_
4 | int m2_
8 | int m3_
12 | int m4_
| [sizeof=16, dsize=16, align=4,
| nvsize=16, nvalign=4]
- Parent m1_從0byte開始
- Parent m2_從4byte開始
- Parent m3_從8byte開始
- Child m4_從12byte開始
子類別本身包含了父類別的成員變數,所以Child的大小為16byte。