C++学习笔记7_多态

1. 类与类之间的关系
class A
{
public:
int a;
void funcA()
{}
}
包含:
class B
{
public:
void funcB(){}
A a;
}
//如果类B有类A的成员变量,那么B has A,类B依赖于类A
使用:
class C
{
public:
void funC(A *a)
{
}
}
//C的成员方法需要A的形参,C use A

*B与A的耦合度比C要高。
//
继承:
class D:public A
{
void funcD()
{

}
D(int a):A(a) //与C#不同的地方,C#是D(int a):base(a),可能是C++有多重继承的原因
{
//..
}
}
*D si A,耦合度最高。
//为什么要写public 呢?应该是确定能否通过D来访问A的成员???

has A,use A, is A;

2.
A->B->C
A->B1->C1
B->D
B1->D
D是多继承

3.类的继承方式

这是和C#不同的地方,C#没有继承方式,继承就是继承了,就算有,C#都是public继承, A中的成员的“外部和继承可访问性”,继承后还是一样。

C++通过继承方式,限制了继承后,改变了原来A“对外和继承的可访问性”。

更多是private和protect的继承方式,对原来A中public的影响最大

*一般用public就行了,其他继承方式不建议使用。

4. 类的兼容原则
class A;
class Aplus:public A;
int main()
{
Aplus ap;
A a=ap;
}
//跟C#类似,能里氏替换,c#子类引用变量能赋值给父类引用变量;
//C++因为子类有父类的所有东西,所以能给父类的所有字段初始化;
//父类指针可以指向子类对象
//父类引用可以引用子类对象
//左父=右子(右子赋值(=号操作符)给左父,右子初始化左父(构造函数))
//子类能够满足父类指针、引用、变量的全部需求。反之不行。

5. 子类构造析构
构造函数,跟C#一样,会先调用父类的构造函数

6. 继承中,子类的成员和父类成员有重名:
子类内部
void func()
{
this->a=1;
A::a=1;//注意,这里要和使用静态的成员区分。
}
//一般都避免这样
//C++的继承其实是很粗暴的,内存的开辟直接堆上去,不管重不重名,有就堆上去。
//C#中,子类的字段不能和父类同名,属性也是,如果非要同名,要加public new int Age{ get;set;}

7.
class A
{
public:
static int a;//静态变量在静态区,继承方式是影响不了其访问性
}
int A::a=100;//静态变量初始化
//
int main()
{
A a;
cout<<a.a<<endl;
}

8. 多继承
//
class Furniture
{
public : int price;
}
class Sofa:public Furniture;
class Bed:public Furniture;
class SofaBed:public Bed,public Sofa;
//
那SofaBed就有两个price空间的大小了(c++粗暴的堆上去),那只能
int main()
{
SofaBed sb;
sb.Bed::price=100;
sb.Sofa::price=200;
}
//这是和C#不同的地方,C#没有多继承

9. 虚继承 (一般很少这样做)
**要不想这样子,给两个price赋值,那么只能:
class Sofa:vitual public Furniture;//加virtual
class Bed:vitual public Furniture;//加virtual
class SofaBed:public Bed,public Sofa;//不变
####出现棱形继承#####
**父类继承爷爷类的时候加virtual
**virtual继承,只是为了开辟“爷类”的字段一次
#区别开C#中的virtual方法,C#中virtual只是用来标记方法,能被子类重新,在以父类或者子类引用的方式调用时,均会执行子类的重写的方法。

10. C++中的虚方法(重要)
class A {
public:
function()
{
//...
}
};
class Aplus:public A
{
public:
function()
{
//...
}
}
//
int main()
{
A *a=new Aplus();//左父右子
a->Function();//这里是调用了A的fuction,不写成虚方法,就执行指针类型的funtion;
}
(这种又叫重定义,调用以A的指针调用function,那么执行A的function;以Aplus的指针调用function,那么执行Aplus的function。)
//这里又和C#不同了,同名方法没有标记为virtual都可以继承。
//C++是父类型指针变量,指向子类对象,能调用子类方法。
***如果想象C#一样,用父类变量引用子类对象,依然可以执行子类的方法。
解决办法:
class A {
public:
vitual function()//虚方法
{
//...
}
};
//这里又和C#不同了,子类不需要在方法前写override
****************但是,习惯上也在子类的方法前加virtual,做一个标识作用,就当做C#中的override****************
//在.cpp文件中,实现虚方法,就不需要加virtual了。

11. 虚析构函数(重要)
防止在使用“左父-右子”的父指针 delete father,如果不标记析构函数为virtual(原因见下),只能析构父类的空间。子类写成virtual析构,能两个析构都调用,不用自己再在子析构中,显式调用父析构。
***特别是用工厂方法new对象时,较为要紧*********
*在正常析构子类对象时,父类对象能自动析构

***********继承也要注意父析构函数标记virtual**********

13. vtpr指针(重要)。
vtpr指针,是指向虚函数表的指针。
例如:A中有virtual fun1(); Aplus重写;
fun1会放到A的虚函数表;fun1会放到Aplus的虚函数表;

A a=new Aplus();
a->fun1();
编译器发现fun1()是虚的,应该会额外标记(重要步骤)。在运行的时候,vtpr指针会去虚函数表找fun1的函数,因为a实质上是Aplus,所以实质上是去找Aplus的虚函数表。
//所以,如果A的fun1没标记为virtual,那么编译自然不会额外标记了,在运行时就只按照A类查找普通的函数表;
//小知识,同样的类,有函数加了virtual,那么sizeof(A)会莫名增大,因为多了vtpr指针的大小。
//不要在父类构造函数中调用virtual函数,否则子类vtpr指针还没初始化好,调的依然是父类的函数,vtpr指针是随着各个父类的构造函数,不断变换指向,直到最后的子类的。

14. 指针步长
A array[] = {Aplus(0),{Aplus(1),{Aplus(2)};//相当于C#A[] array = {new Aplus(0),..};
可以使用array代表是指向第一个元素的地址;
array++可以指向第二个元素;但是,要注意的是,array++是按A的内存大小,往下跳的,如果Aplus所占用的大小大于A,那么array++会跳到一个错误的位置。

15. 纯虚函数,抽象类(C++没有C#中的接口,但是有纯虚函数)
class shape{
public:
double height;
double weight;
virtual int func()=0;//=0只是一个语法,标记为纯虚函数。并不是返回值
shape(){}
virtual ~shape(){}//抽象类最好提供一下虚析构
}
//如果一个类有纯虚函数,那么就是抽象类,不管有没有普通函数。
//跟C#一样,抽象类不能实例化。
//跟C#一样,抽象类能继承抽象类。
/ /跟C#不一样,C#的抽象类必须写成abstract,而且可以写virtual函数,virtual函数能有实现,反而抽象函数,要标记开abstract

class A:public Interface;

猜你喜欢

转载自www.cnblogs.com/pylblog/p/9820779.html