C++学习笔记基础系列2

类之间的关系
继承和派生
1、子类拥有父类的所有成员变量和方法
2、子类可以拥有父类没有的方法和属性
3、子类就是一种特殊的父类
4、子类对象可以当作父类对象使用
多继承的构造和析构,构造:从父类到子类,析构顺序相反;
几种继承关系
public 继承:父类成员在子类中保持原有访问级别
private 继承:父类成员在子类中变为 private 成员
protected 继承:父类中 public 成员会变成 protected
父类中 protected 成员仍然为 protected(子类中可用)
父类中 private 成员仍然为 private (private只能在本类的内部访问,而继承的子类内部和外部都不能使用)
继承中的同名成员变量处理方法
1、当子类成员变量与父类成员变量同名时
2、子类依然从父类继承同名成员
3、在子类中通过作用域分辨符::进行同名成员区分( 在派生类中使用基类的同名成员 ,
显式地使用类名限定符)
4、同名成员存储在内存中的不同位置
派生类中的 static
基类定义的静态成员,将被所有派生类共享
 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访
问性质 (遵守派生类的访问控制)
 派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名 . 成员
多继承:一个类有多个直接基类的继承关系称为多继承
多继承的派生类构造和访问
 多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员
 执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义
派生类时指定的各个继承基类的顺序。
 一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。
如果不同的基类有同名成员,派生类对象访问时应该加以识别。
虚继承
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性
 如果一个派生类从多个基类派生,而这些基类又有一个共同
的基类,则在对该基类中声明的名字进行访问时,可能产生
二义性
 如果在多条继承路径上有一个公共的基类,那么在继承路径的某处
汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象

 要使这个公共基类在派生类中只产生一个子对象,必须对这个基类
声明为虚继承,使这个基类成为虚基类。
 虚继承声明使用关键字 virtual
在这里插入图片描述
如上图,class B其实是B1好B2的共同基类,B中的成员变量b则在C使用时无法得知是从B1继承得到还是B2继承得到(B调用两次构造函数,加了virtual关键字则调用一次),虚继承就可以解决这类问题。
多态一种调用语句 有多种表现形式
 C++中通过 virtual 关键字对多态进行支持
 使用 virtual 声明的函数被重写后即可展现多态特性
在这里插入图片描述
后续编写的代码,可以在之前编写的框架下被调用起来。(接口)
多态成立的三个条件
//1 要有继承
//2 要有虚函数重写
//3 要有父类指针(父类引用)指向子类对象
(多态是设计模式的基础,多态是框架的基础)
多态的理论基础
静态联编和动态联编
1、联编是指一个程序模块、代码之间互相关联的过程。
2、静态联编(static binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
3、动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)。
switch 语句和 if 语句是动态联编的例子。
4、理论联系实际
1、C++与 C 相同,是静态编译型语言
2、在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象;所以编译器认为父类指针指向的是父类对象。
3、由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象
从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。这种特性就是静态联编

虚析构函数
1、内存泄漏例子:A B C三个类 B继承A C继承B ,

public :
	A(){
		p = new char[20];
		strcpy(p,"obja");
	}
	virtual ~A(){
	//在此处添加virtual关键字则可以实现通过父类对象释放子类所有对象
	delete [] p;
}
protected:
private:
char *p;
}
class B:public A{
public :
	B(){
		p = new char[20];
		strcpy(p,"objb");
	}
	~B(){
	delete [] p;
}
protected:
private:
char *p;
}
class C : public B{
public :
	C(){
		p = new char[20];
		strcpy(p,"objc");
	}
	~C(){
	delete [] p;
}
protected:
private:
char *p;
}
void howtodelete(A *base)
//想通过父类指针释放所有子类成员  则需要在基类中的析构函数添加Virtual关键字,否则只释放A
//对象资源
{
	delete base;
}
void main()
{
	C *myc=new C;
	howtodelete(myc);
	return ;
}

重写、重载和重定义
函数重载
必须在同一个类中进行
子类无法重载父类的函数,父类同名函数将被名称覆盖
重载是在编译期间根据参数类型和个数决定函数调用
函数重写
必须发生于父类与子类之间
并且父类与子类中的函数必须有完全相同的原型
重写分为两类:
1、使用 virtual 声明之后能够产生多
2、(如果不使用 virtual,那叫重定义)
多态是在运行期间根据具体对象的类型决定函数调用
C++中多态的实现原理
存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr 指针)
在这里插入图片描述在这里插入图片描述在这里插入图片描述
说明 1:
通过虚函数表指针 VPTR 调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能
确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚
函数的效率要低很多。
说明 2:
出于效率考虑,没有必要将所有成员函数都声明为虚函数
说明 3 :C++编译器,执行 HowToPrint 函数,不需要区分是子类对象还是父类对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值