C++基础知识之"继承中的构造与析构"

fengjingtu

类型兼容性原则

类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的替代包括以下情况:

子类对象可以当作父类对象使用

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

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

父类指针可以直接指向子类对象

父类引用可以直接引用子类对象

在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。

类型兼容规则是多态性的重要基础之一。

总结:子类就是特殊的父类 (base *p = &child;)

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

#include<iostream>


using namespace std;

//类型兼容原则:
//子类对象可以当做父类对象使用
//子类对象可以赋值父类对象使用
//子类对象可以初始化父类对象使用
//父类指针可以直接指向子类对象
//父类引用可以直接引用子类对象

class Parent
{
public:
void printfP()
{
cout << " I am parent" << endl;
}
protected:
private:
int a;
};
class Child:public Parent
{
public:
void printfC()
{
cout << " I am child" << endl;
}
protected:
private:
};
void howtoprint(Parent *p)
{
p->printfP();
}
void howtoprint2(Parent &p)
{
p.printfP();
}
void inherit_plus_test()
{
Parent p1;
p1.printfP();
Child c1;
c1.printfC();

Parent *piontP = NULL;
piontP = &c1;//父类指针可以指向子类对象
piontP->printfP();

Parent &piontP2 = c1;;//父类引用可以引用子类对象
piontP2.printfP();

howtoprint(&p1);
howtoprint(&c1);

howtoprint2(p1);
howtoprint2(c1);


Parent p3 = c1; // 子类对象初始化父类对象
p3.printfP();

}

继承中的对象模型

类在C++编译器的内部可以理解为结构体

子类是由父类成员叠加子类新成员得到的

继承中构造和析构

在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化

在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理

调用原则:

子类对象在创建时会首先调用父类的构造函数

父类构造函数执行结束后,执行子类的构造函数

当父类的构造函数有参数时,需要在子类的初始化列表中显示调用

析构函数调用的先后顺序与构造函数相反

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
class Parent2
{
public:
Parent2(int a, int b)
{
this->a = a;
this->b = b;
cout << " I am parent construct" << endl;
}
void printfP()
{
cout << "a=" << a << " b=" << b << endl;
}
~Parent2()
{
cout << " I am parent xi destroy" << endl;
}
protected:
private:
int a;
int b;
};
class Child2:public Parent2
{
public:
Child2(int a,int b,int c):Parent2(a,b)//父类没有无参构造函数
{
this->c = c;
cout << " I am child construct" << endl;
}
~Child2()
{
cout << " I am child destroy" << endl;

}
void printfC()
{
cout << " c=" <<c<<endl;
}
protected:
private:
int c;
};
void inherit_plus_test2()
{
Child2 c(10, 11, 12);
return;
}
int main_test_inherit()
{
//inherit_plus_test();
inherit_plus_test2();
cout<<"hello world"<<endl;
system("pause");
return 0;
}

继承与组合混合

先构造父类,再构造成员变量、最后构造自己

先析构自己,在析构成员变量、最后析构父类,即:先构造的对象,后释放

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
#include<iostream>
using namespace std;

class Object
{
public:
Object(int a, int b)
{
this->a = a;
this->b = b;
cout << "a=" << a << " b=" << b << endl;
cout << " I am obj construct" << endl;
}
~Object()
{
cout << " I am obj destroy" << endl;
}
protected:
int a;
int b;
};
class Parent3:public Object
{
public:
Parent3(char *p):Object(1,2)
{
this->p = p;
cout << "p=" << p;
cout << " I am parent construct" << endl;
}
void printfP()
{
cout << "I am parent"<< endl;
}
~Parent3()
{
cout << " I am parent destroy" << endl;
}
protected:
char *p;
private:
};
class Child3 :public Parent3
{
public:
Child3(char *cp) :Parent3(cp),o1(3,4),o2(5,6)//父类没有无参构造函数
{
this->cp = cp;
cout << "p=" << cp;
cout << " I am child construct" << endl;
}
~Child3()
{
cout << " I am child destroy" << endl;

}
void printfC()
{
cout << " I am child " << endl;
}
protected:
char * cp;
Object o1;
Object o2;
private:
};
void inherit_plus_test3()
{
char *p = "hello";
Child3 c(p);
//调用顺序:先调用父类构造函数,(如果父类仍有父类,那就先调用),然后再调用组合的构造函数
//析构的过程与构造正好相反
return;
}
int main_inherit_plus_test3()
{
inherit_plus_test3();
cout<<"hello world"<<endl;
system("pause");
return 0;
}

继承中的同名成员变量

1、当子类成员变量与父类成员变量同名时

2、子类依然从父类继承同名成员

3、在子类中通过作用域分辨符::进行同名成员区分(在派生类中使用基类的同名成员,显式地使用类名限定符) 默认情况下是子类的成员变量。

4、同名成员存储在内存中的不同位置

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

#include<iostream>

using namespace std;

class A
{
public:
int a;
int b;
public:
A(int a,int b)
{
this->a = a;
this->b = b;
}
void get()
{
cout << "A::b=" << b << endl;
}
protected:
private:
};
class B :public A
{
public:
int b;
int c;
B(int a,int b1,int b2, int c):A(a,b1)
{
this->b = b2;
this->c = c;
}
void get()
{
cout << "B::b=" << b << endl;
}
protected:
private:
};
void inherit_plus_test4()
{
B b(1,2,3,4);
b.A::b = 100;
b.B::b = 200;

b.get();
b.A::get();
//默认情况下,是使用子类
//使用域作用符
return;
}
int main_inherit_plus_test4()
{
inherit_plus_test4();
cout << "hello world" << endl;
system("pause");
return 0;
}

派生类中的static关键字

继承和static关键字在一起会产生什么现象哪?

基类定义的静态成员,将被所有派生类共享,根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 (遵守派生类的访问控制)

派生类中访问静态成员,用以下形式显式说明:

类名:: 成员

或通过对象访问 对象名 . 成员

总结:

static函数也遵守3个访问原则

static易犯错误(不但要初始化,更重要的显示的告诉编译器分配内存)

构造函数默认为private

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

#include<iostream>
using namespace std;

class C
{
public://如果不写默认是私有的,都不可以继承,只有在单例模式下需要这样
C()//添加构造函数
{
cout << "c的构造函数" << endl;
}
public:
static void Add();
static int i;
};
int C::i = 100;//如果没有使用就不会有问题,可以不写这句话
//这句话是分配内存的过程
void C::Add()
{
i++;
}
class D :private C
{
public:
int b;
int c;
public:
D()//:C()可以写可以不写
{
cout << "d的构造函数" << endl;
}
void getD()
{
cout << "b=" << b << endl;
cout << "c=" << c << endl;
cout << "i=" << i << endl;//可以在子类中被访问

}
void printD()
{
cout << "DDD" << endl;
}


};
void static_inherit_test()
{
D d;
//d.i = 101;//不可以在类外被访问
return;
}
//static关键字仍然遵守派生类的访问控制规则
int main_static_inherit_test()
{
static_inherit_test();
cout<<"hello world"<<endl;
system("pause");
return 0;
}