- C++ 三大特性
- 访问权限
- 1. 继承
- 2. 封装
- 3. 多态
- 虚函数(virtual)
- 虚函数的一些基本概念
- 为什么需要虚继承
- 空类
- 空类的大小不为0,sizeof 为1
- 抽象类与接口的实现
访问限定符: public, protected, private 控制成员变量和成员函数的访问权限
- 类内不存在访问权限问题,类外只能访问public 成员;
- 无论是公有继承、私有继承或保护继承,私有成员不能被派生类访问,public 和 protected可以;
- 对于共有继承,只有基类中的共有成员能够被 派生类对象 访问,保护和私有成员不能被访问。
- 对于私有和保护继承,基类中的所有对象均不能被 “派生类对象” 访问。
定义:
让某种类型的对象获得另一个类型对象的属性和方法
功能:
它可以使用现有的类的所有功能,并在无需重新编写原来类的情况下,对这些功能进行扩展
常见的继承有三种方式:
1. 实现继承: 使用基类的属性和方法,而无需额外编码的能力
**2. 接口继承: ** 仅使用属性和方法的名称,但是子类必须提供实现的能力
3. 可视继承: 子窗体(类)使用基窗体(类)的外观和实现代码的能力
例如:
将⼈定义为⼀个抽象类,拥有姓名、性别、年龄等公共属性,吃饭、睡觉等公共⽅法,在定义⼀个具体的⼈时,就可以继承这个抽象类,既保留了公共属性和⽅法,也可以在此基础上扩展跳舞、唱歌等特有⽅法。
2. 封装
定义:
数据和代码捆绑在一起,避免外界干扰和不确定性访问;
功能:
把可观事物封装为一个抽象类,并且类可以把自己的数据和方法只让可信的类或者对象 *** 作,对不可信的进行信息隐藏,例如:将公共的数据或方法使用 public 修饰,而不希望被访问的数据或者方法用 private 修饰
3. 多态定义:
同一事物表现出不同事物的能力,即向不同对象发送同一消息,不同的对象在接收时会产生不同的行为**(重载实现编译时多态,虚函数实现运行时多态)**
功能:
允许将子类类型的指针 赋值 给父类类型的指针。
实现多态有两种方式:
- 覆盖(override): 即虚函数
- 重载(overload): 允许多个参数不同的同名函数
例如:
基类是一个抽象对象——人,那学生、老师等等都是人。
虚函数(virtual)当基类希望派生类定义适合自己的版本,那么就将这些函数声明为 虚函数
虚函数依赖虚函数表工作,表来保存虚函数的地址,当我们用基类指针指向派生类时,虚表指针指向派生类的虚函数表
这个机制可以保证,派生类中的虚函数可以被调用到
虚函数的一些基本概念1. 虚函数是动态绑定的
使用虚函数的指针或者引用,能够找到实际类的对应函数,而非执行定义类的函数,这是虚函数的基本功能。
2. 多态 : 不同继承关系的类对象,调用同一函数产生不同的行为
- 调用函数的对象必须是指针或引用
- 被调用的函数必须是 虚函数 , 且完成了虚函数的重写。
3. 动态绑定绑定的是动态类型
所对应的函数或属性依赖于对象的动态类型,发生在运行期
4. 构造函数不能是虚函数
5. 虚函数的工作方式
依赖虚函数表工作的,表来保存虚函数的地址,当我们用基类指针指向派生类时,虚表指针指向派生类的虚函数表。
6. 析构函数可以是虚函数,并且,在一个复杂类结构中,析构函数往往大多都为虚函数
7. 将一个函数定义为纯虚函数
实际上这个 *** 作是通过,将这个类定义为抽象类,不能实例化对象;
纯虚函数通常没有定义体,但是也完全可以拥有。
8. inline, static, constructor 三种函数都不能带有virtual 关键字
(1) inline 在编译时展开,必须要有实体
(2) static 属于类自身的类相关,必须有实体
static 成员没有this 指针,而虚函数是通过对象来调用,有隐藏的this 指针。
(3)虚函数是基于内存空间的(vtable), constructor 如果是虚的,那么调用时也需要到内存空间中寻找,但是constructor是虚的情况下是找不到的,因为constructor 自身都不存在了,创建不到class的实例,没有实例,class的成员都不能访问。
9. 析构函数可以是纯虚的
但是析构函数必须有钉一体,因为析构函数的调用是在子类中隐去的
10. 派生类的override虚函数定义必须与父类完全一致
除了一个特例,如果父类返回值是一个指针或者引用,子类override时,可以返回这个指针或引用的派生。
为什么需要虚继承1. 为了解决多继承时的命名冲突和冗余数据问题
C++提出了虚继承,使得在派生类中只保留一份间接基类的成员。其中多继承是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。
2. 虚继承的目的是让某个类做出声明,承诺愿意共享它的基类
其中,被共享的基类称为 虚基类(Virtual Base Class).
使用多继承经常出现二义性,必须十分小心:
一般只有在比较简单和不易出现二义性的情况下,或者要求使用的情况下 才会使用多继承,能用单一继承解决的问题,一般不使用多继承。
空类 空类的大小不为0,sizeof 为1类的实例化是在内存中分配一块地址,因此每个实例在内存中均有独一无二的地址。
同样,空类也会实例化,所以编译器会给空类隐含的添加一个地址,这样就可以保证空类在实例化之后就有独一无二的地址,所以空类的sizeof 为1 ,而不是0
class A {
virtual void f() {}
};
class B: public A{}
注意:此时类A和类B都不是空类,其sizeof 都为4,因为他们都具有虚函数表
class A {};
class B:public virtual A{};
此时,类A是空类,其大小为1,; 而类B不为空类,其sizeof 是4.因为含有虚函数表的地址。
多重继承的空类的大小也为1.
class Father1{};
class Father2{};
class Child: public Father1, public Father2{};
Father1
, Father2
, Child
的大小均为1。
何时共享虚函数地址表:
如果派生类继承的第一个是基类,且该基类定义了虚函数地址表,则派生类就共享该表首地址占用的存储单元。
class X{}; //sizeof(X):1
class Y : public virtual X {}; //sizeof(Y):4
class Z : public virtual X {}; //sizeof(Z):4
class A : public virtual Y {}; //sizeof(A):8
class B : public Y, public Z{}; //sizeof(B):8
class C : public virtual Y, public virtual Z {}; //sizeof(C):12
class D : public virtual C{}; //sizeof(D):16
抽象类与接口的实现
接口描述了类的行为和功能,而不需要完成类的特定实现; C++接口是使用抽象类来实现的。
- 类中至少有一个函数被声明为虚函数,则这个类就是抽象类。纯虚函数是通过在声明中,使用 “= 0 ”来指定的。
- 设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能用于实例化,它只能作为接口使用。
class Shape
{
public:
// 提供接⼝框架的纯虚函数
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派⽣类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
主函数:
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
Rect.getArea(); //35
Tri.setWidth(5);
Tri.setHeight(7);
Tri.getArea(); //17
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)