【C++进阶】继承

目录

继承的概念和定义

继承中的作用域

派生类的默认成员函数

构造函数:

析构函数:

赋值&析构函数

复杂的菱形继承及菱形虚拟继承

继承的总结和反思


继承的概念和定义

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter";
	int _age = 18;
};
//Student继承了Person,Student中就拥有了Person的成员
//person 叫父类/基类
//student叫子类/派生类
class Student :public Person
{
protected:
	int _stuid;
};
class Teacher :public Person
{
protected:
	int _Jobid;
};
int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

继承中的作用域

B中的fun和A中的fun不是构成重载,因为不是在同一个作用域

B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏

//A和B类的两个fun构成隐藏关系,继承中函数名相同就是隐藏
class  A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B :public A
{
public:
	void fun(int i)
	{}
};
void Test()
{
	B b;
	b.fun(10);
	b.fun();
	//想调用父类的=>b.A::fun()
}
int main()
{
	Test();
	return 0;
}

派生类的默认成员函数

构造函数:

要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化

class Person
{
public:
	//Person(const char* name = "peter")
	Person(const char* name )
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student :public Person
{
public:
	//子类构造函数——我们不写,编译器默认生成
	//1.继承的父类成员---作为一个整体调用父类的构造函数初始化
	//2.自己的内置类型成员---不处理(除非给了缺省值)
	//3.自己的自定义类型成员---调用它的默认构造函数

	//我们要自己实现子类构造函数
	Student(const char*name, int id, const char* address)
	//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
	:Person(name)
	, _id(id)
	, _address(address)
	{}
private:
	int _id;//内置类型
	string _address;//自定义类型
};
int main()
{
	Student s("黄振盛",1,"厦门市");
	return 0;
}

析构函数:

class Person
{
public:
	Person(const char* name = "peter")
	//Person(const char* name )
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student :public Person
{
public:
	//子类拷贝构造函数——我们不写,编译器默认生成
	//1.继承的父类成员作为一个整体---调用父类的拷贝构造
	//2.自己的内置类型成员---调用它的拷贝构造
	//3.自己的自定义类型成员---值拷贝
	Student(const char*name, int id, const char* address)
		//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
		:Person(name)
		, _id(id)
		, _address(address)
	{}
	//自己实现拷贝构造
	Student(const Student& s)
		:Person(s)
		,_id(s._id)
		, _address(s._address)
	{}
private:
	int _id;//内置类型
	string _address;//自定义类型
};
int main()
{
	Student s1("小红",100,"厦门市");
	Student s2(s1);
	return 0;
}

赋值&析构函数

class Person
{
public:
	//Person(const char* name = "peter")
	Person(const char* name )
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student :public Person
{
public:
	//子类拷贝构造函数——我们不写,编译器默认生成
	//1.继承的父类成员作为一个整体---调用父类的拷贝构造
	//2.自己的内置类型成员---调用它的拷贝构造
	//3.自己的自定义类型成员---值拷贝
	Student(const char*name, int id, const char* address)
		//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
		:Person(name)
		, _id(id)
		, _address(address)
	{}
	自己实现拷贝构造
	//Student(const Student& s)
	//	:Person(s)
	//	,_id(s._id)
	//	, _address(s._address)
	//{}
	//赋值:
	Student& operator = (const Student& s)
	{
		if (this != &s)
		{
			_id = s._id;
			_address = s._address;
			Person::operator = (s);
		}
		return *this;
	}
	//析构
	//子类拷贝构造函数——我们不写,编译器默认生成
	//1.继承的父类成员作为一个整体---调用父类的析构函数
	//2.自己的内置类型成员---不处理
	//3.自己的自定义类型成员---调用它的析构函数
	//自己实现
	//子类析构函数和父类析构函数构成隐藏关系--why?
	//因为编译器会对析构函数名做特殊处理,所有类的析构函数都会被统一处理成destructor()
	//为什么编译器会做这个处理呢?——析构函数要构成多态重写
	~Student()
	{
		Person::~Person();//子类的析构函数在执行结束后会自动调用父类的析构函数
	}
private:
	int _id;//内置类型
	string _address;//自定义类型
};
int main()
{
	Student s1("小红",100,"厦门市");
	Student s2(s1);
	Student s3("小蓝",200,"泉州市");
	s1 = s3;
	return 0;
}

复杂的菱形继承及菱形虚拟继承

先来看一段代码:

class B : public A
{
public:
	int _b;
};
// class C : public A
class C : public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	//d._a = 0;
	return 0;
}

 再看一段虚继承代码:

class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A
{
public:
	int _b;
};
// class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	d._a = 0;
	return 0;
}

 将上面两段内存代码对比如下:

存了个偏移量 

 

访问继承的虚基类对象成员,都是取偏移量计算

小结:

多继承带来的问题是菱形继承,菱形继承解决引来了虚拟继承,虚拟继承在腰部的位置!

如何解决:偏移量!

继承的总结和反思

1.最好不要设计出菱形继承

2.public继承是一种 is-a 的关系,组合是一种 has-a 的关系,尽量使用耦合度低(模块(类..)间关联度越低,独立性越高,耦合度越低)的组合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值