C++继承

一.继承相关概念


二.派生类的定义


代码举例如下:

class Parent//基类
{
private:
	int a;
public:
	void print()
	{
		cout << "a=" << a << endl;
	}
protected:
};
class Child :public Parent//派生类,这里的public 可以也可以是private或者protected
{
private:
	int b;
public:
protected:
};
三.继承的重要说明:

1.子类拥有父类的所有成员变量和成员函数

2.子类就是一种特殊的父类

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

4.子类可以拥有父类没有的方法和属性

四.派生类的访问控制:

1.如若是public继承,父类在子类中保持原有访问类别

2.如若是protect继承,父类的public变为protected,private依然是private,protected依然是protected

3.如若是private继承,父类在子类中都是private

对于1的代码测试如下:

①public继承

class Parent//基类
{
private:
	int a;
public:
	int b;
protected:
	int c;
};
class Child :public Parent//public继承
{
private:
public:
	void fun()
	{
		a = 10;//no
		b = 10;//yes
		c = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用
	}
protected:
	
};
int main()
{
	Child c1;
	c1.a = 10;//no
	c1.b = 10;//yes
	c1.c = 10;//no
}
②private继承
class Parent//基类
{
private:
	int a;
public:
	int b;
protected:
	int c;
};
class Child :private Parent//private继承过来以后在子类中改变了访问属性
{
private:
public:
	void fun()
	{
		a = 10;//no
		b = 10;//yes
		c = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用
	}
protected:
	
};
int main()
{
	Child c1;
	c1.a = 10;//no
	c1.b = 10;//no
	c1.c = 10;//no
}
③protected继承
class Parent//基类
{
private:
	int a;
public:
	int b;
protected:
	int c;
};
class Child :protected Parent//private继承过来以后在子类中改变了访问属性
{
private:
public:
	void fun()
	{
		a = 10;//no
		b = 10;//yes
		c = 10;//yes
	}
protected:
	
};
int main()
{
	Child c1;
	c1.a = 10;//no
	c1.b = 10;//no
	c1.c = 10;//no
}
五.继承中的构造和析构

1.类型兼容性原则:在需要基类对象的任何地方都可以使用公有子类对象代替,通过公有继承派生类得到了基类出构造和析构外的所有成员

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

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

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

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

⑤父类引用可以引用子类对象

class Parent//基类
{
private:
	int a;
public:
	void print()
	{
		cout << "我是父类" << endl;
    }
protected:
};
class Child :public Parent
{
private:
	int c;
public:
protected:
};
int main()
{
	Child c1;
	Parent *p = NULL;
	p = &c1;//父类指针指向子类对象
	p->print();//调用父类函数

	Parent &n = c1;//子类引用初始化父类
	n.print();//打印父类函数
	system("pause");
	return 0;
}
六.继承中的对象模型和继承中的构造和析构

问题的引出,首先看下面的代码:


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

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

继承中构造和析构的调用原则:

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

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

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

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

代码举例如下:

class parent
{
public:
	parent(int x, int y)
	{
		this->a = x;
		this->b = y;
		cout << "父类构造函数" << endl;
	}
	~parent()
	{
		cout << "父类析构函数" << endl;
		system("pause");
	}
private:
	int a;
	int b;
};
class child:public parent
{
public:
	child(int x, int y, int z) :parent(x,y)//因为父类构造函数中有参数,所以需要用参数初始化列表显示调用
	{
		this->c = z;
		cout << "子类构造函数" << endl;
	}
	~child()
	{
		cout << "子类析构函数" << endl;
		
	}
private:
	int c;
};
void test()//为了更好的显示对象的周期
{
	child c1(1, 2, 3);
}
int main()
{
	test();
	system("pasue");
	return 0;
}
刚好符合上面的规则

七.继承与组合混搭情况下的构造和析构的调用原则

原则:

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

先析构自己,再析构成员变来那个,最后析构父类

代码如下:

class grandfather
{
private:
	int ch;
	int xl;
public:
	grandfather(int w,int m)
	{
		this->ch = w;
		this->xl = m;
		cout << "ch=" << ch << "  xl=" << xl << endl;
		cout << "执行爷爷的构造函数"<<endl;
	}
	~grandfather()
	{
		cout << "执行爷爷的析构函数" << endl;
		
	}
};
class parent :public grandfather
{
public:
	parent(int w,int m,int x, int y) :grandfather(w,m)
	{
		this->a = x;
		this->b = y;
		cout << "a=" << a << "  b=" << b << endl;
		cout << "父类构造函数" << endl;
	}
	~parent()
	{
		cout << "父类析构函数" << endl;
	}
private:
	int a;
	int b;
};
class child:public parent
{
public:
	child(int x, int y, int m, int w, int z) :parent(x, y, m,w), obj1(x,y),obj2(3,4)//因为父类构造函数中有参数,所以需要用参数初始化列表显示调用
	{
		this->c = z;
		cout << "c=" << z << endl;
		cout << "子类构造函数" << endl;
	}
	~child()
	{
		cout << "子类析构函数" << endl;
	}
private:
	int c;
	grandfather obj1;
	grandfather obj2;
};
void test()//为了更好的显示对象的周期
{
	child c1(1,2,3,4,5);
}
int main()
{
	test();
	system("pause");
	return 0;
}

八.继承中同名成员变量和函数的处理方法

方法:通过作用域区分符去区分

1.重名成员


测试代码如下:

class parent
{
public:
	int a;
	int b;
	void print()
	{
		cout << "a=" << a << endl;
	}
};
class child :public parent
{
public:
	int a;
	int b;
	void get()
	{
		cout << "a=" << a << endl;
	}
};
int main()
{
	child c2;
	c2.a = 20;
	c2.get();//调用子类函数
	c2.print();//调用父类函数
	system("pause");
	return 0;
}

2.重名函数


测试代码:

class parent
{
public:
	void print()
	{
		cout <<"父类打印函数"<< endl;
	}
};
class child :public parent
{
public:
	void print()
	{
		cout <<"子类打印函数" << endl;
	}
};
int main()
{
	child c2;
	c2.print();
	system("pause");
	return 0;
}
所以默认是子类的函数。

十.派生类的static关键字

下面将用代码实例来说明:

class parent
{
public:
	parent()
	{
		cout << "父类构造函数" << endl;
	}
	void print()
	{
		cout <<"父类打印函数"<< endl;
	}
public:
	static int a;
};
int parent::a = 100;//告诉编译器给static成员分配空间,告诉编译我在继承中用到a,不然会报错
class child :public parent//私有继承
{
public:
	void print1()
	{
		cout << a << endl;
		cout <<"子类打印函数" << endl;
	}
};
int main()
{
   //①static也遵循派生类访问控制,私有继承,所以外界不可访问
	//child c1;
	//c1.a = 200;//不可访问
	//② int parent::a = 100;//告诉编译器给static成员分配空间,告诉编译我在继承中用到a,不然会报错
	  //现在将int parent::a=100屏蔽开始测试
	//child c1;
	//c1.print();//可以通过
	//c1.print1();//无法通过,因为没有给a分配内存空间加上int parent::a=100就可以通过
	system("pause");
	return 0;
}
十一.多继承

1.

代码实例:

class base1
{
private:
	int a;
public:
	base1(int a)
	{
		this->a = a;
	}
	void print1()
	{
		cout << this->a << endl;
	}
};
class base2
{
private:
	int b;
public:
	base2(int b)
	{
		this->b= b;
	}
	void print2()
	{
		cout << this->b<< endl;
	}
};
class stu:public base1,public base2
{
private:
	int c;
public:
	stu(int a, int b, int c) :base1(a), base2(b)
	{
		this->c = c;
	}
	void print3()
	{
		cout << this->c << endl;
	}
};
int main()
{
	stu c1(1, 2, 3);
	c1.print1();
	c1.print2();
	c1.print3();
	system("pause");
	return 0;
}
2.多继承二义性(虚继承)
对于二义性的解释:


代码如下:

class A
{
public:
	int z;
};
class base1:public A
{
public:
	int a;
};
class base2:public A
{
public:
	int b;
};
class stu:public base1,public base2
{
public:
	int c;
};
int main()
{
	stu c1;
	c1.z;//访问z就会出问题
	system("pause");
	return 0;
}

怎么解决上述问题呢?这就需要用到虚继承,给的公有继承public前面加上关键字virtual就解决了

class base1:virtual public A
{
public:
	int a;
};
class base2:virtual public A
{
public:
	int b;
};

编译就可以通过了。

不过虚继承只能解决这种有公有继承祖先的多继承,菱形继承,比如下面这个例子加上virtual就无法解决

class B1
{
public:
	int a;
};
class B2
{
public:
	int a;
};
class C :virtual public B1, virtual public B2
{
public:
};
int main()
{
	C c1;
	c1.a;//编译出错,访问不明确,如下图
	system("pause");
	return 0;
}

这种问题只能通过我们自己解决,加上域限制符

class B1
{
public:
	int a;
};
class B2
{
public:
	int a;
};
class C : public B1,public B2
{
public:
};
int main()
{
	C c1;
	c1.B1::a=100;
	c1.B2::a=200;
	system("pause");
	return 0;
}

这样就可以了。

补充,在菱形继承中加上virtual和不加vitual的父类大小有区别吗?接下来我们做个实验

代码如下:

class A
{
public:
	int z;
};
class base1:virtual public A//加上virtual
{
public:
	int a;
};
class base2:public A//没有加virtual
{
public:
	int b;
};
class stu:public base1,public base2
{
public:
	int c;
};
int main()
{
	cout << sizeof(A) << endl;
	cout << sizeof(base1) << endl;//加上virtual
	cout << sizeof(base2) << endl;//没有加virtual
	system("pause");
	return 0;
}

由运行结果可以看出,加上virtual的编译器给变量偷偷的增加了属性,这就是虚继承的一种手段~

具体为什么会多了四个字节,增加的 属性是什么呢?接下来我们具体分析

我就直接开门见山的说了,解释由下面图示,首先从内存看起



这就是虚继承的本质!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值