一、私有继承
1.两个类之间的继承关系为私有,编译器一般不会讲派生类对象转换成基类对象
2.此部分私有基类继承而来的成员都成为了派生类的私有成员。
#include
using namespace std;
class Person
{
public:
void eat()
{
cout<<"Person eat"<
}
};
class Student : private Person //私有继承
{
public:
void study()
{
cout<<"Student Study"<
}
};
int main()
{
Person p;
Student s;
p.eat();
s.study();
s.eat(); //编译错误
p = s; //编译错误
return 0;
}
二、私有继承和组合的区别
#include
using namespace std;
class Engine
{
public:
Engine(int num): numCylinders(num){}//Engine构造函数
void start()
{
cout<<"Engine start,"<
}
private:
int numCylinders;
};
class Car_pri :private Engine
{
public:
Car_pri() : Engine(8){ } //调用基类的构造函数
voit start()
{
Engine::start(); //调用基类的start()
}
};
class Car_comp
{
private:
Engine engine; //组合Engine类对象
public:
Car_comp:engine(8){} //给engine成员初始化
void start()
{
engine.start(); //调用engine的start()
}
};
int main()
{
Car_pri car_pri;
Car_comp car_comp;
car_pri.start();
car_comp.start();
return 0;
}
类Car_pri和类Car_comp有相似点
1.他们都只有一个Engine被确切的包含在Car中
2.他们在外部都不能进行指针转换,如讲Car_pri*转换为Engine*
3.它们都有一个start()方法,并且都在包含的Engine对象中调用start()方法
区别:
1.如果想要每个Car都包含若干Engine,那么只能用单一组合的形式
2.私有继承形式可能引入不必要的多重继承
3.私有继承形式允许Car的成员将Car*转换成Engine*
4.私有继承形式允许访问基类的保护成员
5.私有继承形式允许Car重写Engine的虚函数
原则:尽可能使用组合,万不得已采用私有继承
三、C++虚函数实现的细节
如果一个类中含有虚函数,那么系统会为这个类分配一个指针成员指向一张虚函数表(vtlb),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组。
int main()
{
Car_pri car_pri;
Car_comp car_comp;
car_pri.start();
car_comp.start();
return 0;
}
class Parent
{
public:
virtual void foo1() {}
virtual void foo1() {}
void foo3();
};
class Child1
{
public:
void foo1(){}
void foo3();
};
class Child2
{
public:
void foo1(){}
void foo2(){}
void foo3();
};
parent类的vtlb:Parent::foo1()的地址,Parent::foo1()
Child1类的vtlb: Child1::foo1()的地址,Parent::foo1()
Child2类的vtlb: Child1::foo1()的地址,Child2::foo1()
虚函数表既具有继承性,又有多态性,每个派生类的vtlb继承了它各自基类的vtlb,如果基类vtlb中包含某一项,则其派生类的vtlb中也将包含同样的一项,但是两项的值可能不同,如果派生类覆盖了该项对应的虚函数,则派生类vtlb的该项指向重载后的虚函数,没有重载的话,则沿用基类的值
四、C++虚拟机制
在构造函数中,虚拟机制不会发生作用
#include
using namespace std;
class A
{
public:
virtual void print(void)
{
cout<<"A::print()"<
}
};
class B:public A
{
public:
virtual void print(void)
{
cout<<"B::print()"<
}
};
class C:public A
{
public:
void print(void)
{
cout<<"c::print()"<
}
};
void print(A a)
{
a.print();
}
void main(void)
{
A a,*pa,*pb,*bc;
B b;
C c;
pa = &a;
pb = &b;
pc = &c;
a.print();
b.print();
c.print();
pa->print();
pb->print();
pc->print();
print(a);
print(b);
print(c);
}
前三个是调用各自类的print成员函数
中间三个是调用各自类的print成员函数
最后三个由于是传值,则会在函数栈中分别生成类A的临时对象,所以执行的都是类A的print函数,如果是传引用,则又是多态
#include
#include
using namespace std;
void println(const std::string &msg)
{
cout<
class Base
{
public:
Base()
{
println("Base::Base()");
virt();
}
void f()
{
println("Base::f()");
virt();
}
virtual void virt()
{
println("Base::virt()");
}
};
class Derived:public Base
{
public:
Derived()
{
println("Derived::Derived()");
virt();
}
virtual void virt()
{
println("Derived::virt()");
}
};
int main(int argc, char * argv[])
{
Derived d;
Base *pB=&d;
pB->f();
return 0;
}
五、多重继承的二义性
#include
class cat
{
public:
void show()
{
cout<<"cat"<
}
};
class fish
{
public:
void show()
{
cout<<"fish"<
}
};
class catfish:public cat,public fish
{
};
int main()
{
catfish obj;
obj.show();
return 0;
}
这里调用哪个show都会出现错误,因此,可以改成obj.car::show()访问car的成员
六、多重继承和虚拟继承
多继承中的构造函数顺序
1、任何虚拟基类的构造函数按照它们被继承的顺序构造
2、任何非虚拟基类的构造函数按照它们被构造的顺序构造
3、任何成员对象的构造按照它们声明的顺序构造
4、类自身的构造函数
七、抽象基类和纯虚函数
1、为了方便使用多态特性
2、在多数情况下,基类本身生成对象是不合理的,抽象基类不能被实例化,它定义的纯虚函数相当于借口,能把派生类的共同行为提取出来
3、抽象类不能实例化
阅读(1044) | 评论(0) | 转发(0) |