C++基础 06

本文详细介绍了 C++ 中的继承控制权限、兼容性原则、构造和析构顺序、子父类变量和函数调用、静态变量使用、多继承及多态等内容,并通过实例演示了这些概念的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.继承控制权限

class Perpon
{
public:
	int a;
private:
	int b;
protected:
	int c;
};

//学生类继承了perpon类,在c++中是一个“:”表示继承关系;
//在c++中继承有三种权限控制;public protected private;
//public继承和java中的继承是一样的
//protected继承会吧父类中的public成员在子类中变成protected,只能在本类的内部使用,不能再外部使用;
//private 继承会吧父类中的成员全部点成私有的,但是除了父类中定义的私有属性外,全都可以在子类的内部使用;在外部不能使用;
class Student : private Perpon
{
public:
	int m;
	void aaa()
	{
		a = 100;
		//b = 200;//err
		c = 100;
	}
private:
	int n;
protected:
	int p;
};


void main(){
	
	system("pause");
}

2.继承的兼容性原则

class Parent
{
public:
	void printfP()
	{
		cout << "i am parent" << endl;
	}
};


class Child : public Parent
{
public:
	void printfC()
	{
		cout << "i am child" << endl;
	}
};


void main(){
	Parent t;
	Child c;
	t = c;//子类可以直接赋值给父类;只赋值父类中的属性;

	//继承兼容性原则一:
	//父类指针(引用)可以指向子类对象;(除私有继承)
	Parent* p = NULL;
	p=&c;//如果是私有继承这里会报错;
	p->printfP();
	Parent& b = c;
	b.printfP();

	//兼容性原则二:
	//用子类可以初始化父类;子类也是一种特殊的父类;
	Parent t1 = c;//会调用t1的拷贝构造;
	system("pause");
}

3.继承中的构造和析构的调用

class Parent
{
public:
	Parent(int a)
	{
		cout << "Parent de 构造" << endl;
		this->a = a;
	}

	~Parent()
	{
		cout << "parent de xigou" << endl;
	}
private :
	int a;
};


class Child : public Parent
{
public:
	//在继承中,初始化子类必须要初始化父类;通过调用父类的构造方法去初始化父类的属性;
	Child(int b,int a) :Parent(a)
	{
		cout << "Child de 构造" << endl;
		this->b = b;
	}
	~Child()
	{
		cout << "Child de xigou" << endl;
	}
private :
	int b;
};

/*
在这里的调用顺序,变量的初始化和析构,父子类是各司其职,各管各的;
Parent de 构造
Child de 构造
Child de xigou
parent de xigou
请按任意键继续. . .
*/
void test()
{
	Child d(1, 2);
}

void main(){
	test();
	system("pause");
}

4.继承中混合继承,成员对象等的构造和析构顺序

#include"iostream"	//包含C++的头文件。
#include"MyString.h"
using namespace std;	//使用标准的std命名空间(在这个空间里面定义了很多的标准定义)
//在iostream中没有引入std命名空间,在此所以要自己写;如果不写则需要显示引入std;


class Object
{
public:
	Object(int a)
	{
		cout << "Object de 构造" << endl;
	}
	~Object()
	{
		cout << "Object de xigou" << endl;
	}

private:
	int a;

};


class Parent :public Object
{
public:
	Parent(int a) :Object(a)
	{
		cout << "Parent de 构造" << endl;
		this->a = a;
	}

	~Parent()
	{
		cout << "parent de xigou" << endl;
	}
private :
	int a;
};


class Child : public Parent
{
public:
	//在继承中,初始化子类必须要初始化父类;通过调用父类的构造方法去初始化父类的属性;
	//初始化父类完后再初始化成员对象,在初始化自身对象;
	//和“:”后面写的顺序没关系
	Child(int b,int a) :obj1(a), Parent(a)
	{
		cout << "Child de 构造" << endl;
		this->b = b;
	}
	~Child()
	{
		cout << "Child de xigou" << endl;
	}
private :
	int b;
	Object obj1;
};

/*
Object de 构造
Parent de 构造
Object de 构造
Child de 构造
Child de xigou
Object de xigou
parent de xigou
Object de xigou
请按任意键继续. . .
*/

//析构的原则就是先初始化的后析构,后初始化的先析构;
void test()
{
	Child c(1,2);
}

void main(){
	test();
	system("pause");
}

5.子父类中重名的变量和函数的调用语法

class Parent 
{
public:
	Parent(int a=100,int b=0)
	{
		cout << "Parent de 构造" << endl;
		this->a = a;
		this->b = b;
	}

	~Parent()
	{
		cout << "parent de xigou" << endl;
	}
	int  getA()
	{
		return a;
	}

	int a;
	int b;
};


class Child : public Parent
{
public:
	//在继承中,初始化子类必须要初始化父类;通过调用父类的构造方法去初始化父类的属性;
	//初始化父类完后再初始化成员对象,在初始化自身对象;
	//和“:”后面写的顺序没关系
	Child(int a,int b)
	{
		cout << "Child de 构造" << endl;
		this->b = b;
		this->a = a;
	}
	~Child()
	{
		cout << "Child de xigou" << endl;
	}
	int  getA()
	{
		return a;
	}
	int a;
	int b;
};



//析构的原则就是先初始化的后析构,后初始化的先析构;
void test()
{
	//在C++中子类和父类中的变量和函数是允许重名的,而且还可分别使用,调用语法如下;
	Child c(1,2);
	cout << c.b << endl;//默认调用子类
	cout << c.Parent::b << endl;//调用父类的;
	cout << c.getA() << endl;
	cout << c.Parent::getA() << endl;
}

void main(){
	test();
	system("pause");
}


6.静态变量在继承中的使用:

class Parent 
{
public:
	//构造函数不写权限符,默认是私有的;
	Parent(int a=100,int b=0)
	{
		cout << "Parent de 构造" << endl;
		this->a = a;
		this->b = b;
	}
	~Parent()
	{
		cout << "parent de xigou" << endl;
	}
	int  getA()
	{
		return a;
	}

	static int a;//静态成员变量是属于类的;必须显示出事话;
	static int c;
	int b;
};
//显示定义a;在C++中静态是属于类的,在创建对象之前先分配内存,创建对象的时候是不给静态变量分配内存的
//虽然你静态变量的代码写在类的内部,会想像普通变量创建对象分配内存,静态变量可以在创建对象的时候赋值;
//静态变量是子父类公用的一个变量;共享的;
int Parent::a;
int Parent::c=123;

class Child : public Parent
{
public:

	Child(int a,int b)
	{
		cout << "Child de 构造" << endl;
		this->b = b;
		this->a = a;
	}
	~Child()
	{
		cout << "Child de xigou" << endl;
	}
	int  getA()
	{
		return a;
	}
	int a;
	int b;
};


void test()
{
	Child c(1,2);
	c.c = 456;
	cout << c.c << endl;
	cout << Parent::c << endl;//可以通过类名调用
}

void main(){
	test();
	system("pause");
}


7.多继承以及存在的问题、

class A {
public:
	int a;
};

class B1 : virtual public A
{
public:
	int b1;
};

class B2 :virtual public A
{
public:
	int b2;
};

class C :public B1,public B2
{
public:
	int c;
};

void main(){
	C  c;
	c.a = 100;//如果不加virtual的话,编译器就无法辨识是继承谁得到的属性a;
	system("pause");
}
另一种情况:

class B1
{
public:
	int b;
};

class B2 
{
public:
	int b;
};

class C : public B1,  public B2
{
public:
	int c;
};

void main(){
	cout << sizeof(C) << endl;//12个字节
	C  c;
	//如果不加virtual的话,编译器就无法辨识是继承谁得到的属性b;
	//在继承的时候加virtual,发现还是报错,还是无法辨识;
	//这种情况需要显示调用
	c.B1::b = 100;
	system("pause");
}

8.C++中的多态

多态的概述:同一种调用,有多种调用形态,称为多态:
void hahah(Parent * p)
{
	//下面这个行代码会有不同的调用关系,就看你传进来的是啥了
	//如果p指向的是Parent,那么调用的是Parnet的get方法;
	//如果p指向的是Child,在child类中没有重写这个get方法,调用的还是Parent的逻辑代码;
	//如果p指向的是Child,在child类中重写了这个get方法同时父类中的get方法也加了virtual关键字,那么调用的还是Child的get逻辑代码;
	p->get();
}

C++的多态 和 Java中的多态
java中:
        // (即:继承后子父类对象都能调用,但是子类重写后调用就是子类的逻辑代码,子类重写调用就是父类的逻辑代码)
C++中:在C++中重写概念,所以即使你像java中,在子类中明着把继承下来的方法写出来,那也不是重写,去调用这个方法,执行的还是父类方法中的逻辑代码;解决方法是在父类的那个要被继承方法中加一个关键字virtual,这个关键字的意思是“虚”,加了之后就是虚函数了;
class B1
{
public:
	int b=100;
	virtual int getB()
	{
		return b;
	}
};

class B2 :public B1
{
public:
	int b=200;
	int getB()
	{
		return b;
	}
};



void main(){
	B2 bbbbb;
	B1 * b = &bbbbb;
	//在这里java中父类调用被继承和重写的方法后执行的时候子类的重写的逻辑代码
	//但是在C++中这里不是,这里还是调用了父类的逻辑代码;就这一种情况,其他继承和java是一样的;解决方法是在父类的这个方法上加一个virtual;
	cout<< b->getB()<<endl;
	system("pause");
}
面向对象的三大概念:
  1. 封装:是用类或者方法,把固有的业务逻辑代码进行封装,在使用对象做函数参数,在任何地方都可以调用;
  2. 继承:继承家族中的代码复用;
  3. 多态:写好接口,调用后来人的代码,实现未来场景;(后来人继承你的基类,重写你的方法);

多态成立的三个条件:继承、重写、父类指针或者引用指向子类对象

9.多态一个典型的案例:

通过父类指针和引用释放所有家族中的内存

class B1
{
public:
	char* name;
	B1()
	{
		name = new char[10];
		cout << "B1()" << endl;
	}
	virtual void get()
	{
		cout << "B1 get" << endl;
	}
	virtual ~B1()
	{
		delete[] name;
		cout << "~B1()" << endl;
	}

};

class B2 :public B1
{
public:
	char* name;
	B2()
	{
		name = new char[10];
		cout << "B2()" << endl;
	}
	 void get()
	{
		cout << "B2 get" << endl;
		B1::get();//相当于java中的super.get();
	}
	~B2()
	{
		delete[] name;
		cout << "~B2()" << endl;
	}
};

class B3 :public B2
{
public:
	char* name;
	B3()
	{
		name = new char[10];
		cout << "B3()" << endl;
	}
	void get()
	{
		cout << "B3 get" << endl;
		B2::get();//相当于java中的super.get();
	}
	~B3()
	{
		delete[] name;
		cout << "~B3()" << endl;
	}
};

void main(){
	//父类指针释放所有子类的中申请的内存,多态析构函数
	//构造析构函数默认都会调用父类的相应的函数,不像在java中在构造函数中必须写super();
	//所以通过调用子类的析构可以把所有的家族中的析构都会调用起来;
	B1 * b;
	b = new B3();
	b->get();
	delete b;
	system("pause");
}
运行结果:

B1()
B2()
B3()
B3 get
B2 get
B1 get
~B3()
~B2()
~B1()
请按任意键继续. . .

10多态的探究:

编译器是怎么知道的通过父类引用调用子类还是父类的继承方法呢??

当类加载到内存中的时候,编译知道了类之间的继承关系,也得到了哪些方法是继承方法,这个时候就建立了一个继承方法表,同时也建立了一个vprt* 指针,子父类是共用这个继承方法表的,当每一定义或者重写都会把函数入口地址添加到表中,刷新这个表;不管一个父类引用指向一个子类对象还是父类对象,这个引用调用方法的时候只会判断这个方法是不是继承方法,是的话通过vprt*指针去找最新的这个函数的入口地址(即重写就是重写的地址,没有重写的话就是原本定义的地址),然后执行方法;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值