目标:
- 继承概念、怎么定义继承和派生类
- 继承关系和访问符
- 创建派生类对象时怎么调用构造析构
- 继承赋值规则
了解单继承、多继承、菱形继承
继承
通过继承联系在一起的类构成一种层次关系。根部是基类,其他类则是直接或间接从基类继承而来,这些类成为派生类。基类的定义的层次关系中所以类都可以拥有的成员,派生类则是定义自己特有成员。
继承呈现了 面向对象程序设
计的层次结构, 体现了 由简单到复杂的认知过程。继承的定义
继承首先要有基类这里定义一个基类
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
void FunTest()
{
cout << "BFunTest()" << endl;
}
public:
int _b2;
protected:
int _b1;
private:
int _b;
};
class+派生类(子类)+:+继承类型+基类(父类)
例:class Derived:public Base{}
继承类型:基类成员在派生类的可见性(public、protected、private)
- 继承关系与访问限定符
- public成员:
可以在派生类中和类外被访问。 - protected:
只能在基类和派生类中被访问。 - private:
基类的private成员在派生类中是不能被访问的, 如果基类成员 不想在类外直接被访问, 但需要
在派生类中能访问, 就定义为protected。 可以看出保护成员限定符是因继承才出现的。 - 使用关键字class时默认的继承方式是private, 使用struct时默认的继承方式是public, 不过最
好显示的写出继承方式。
另外如下
- public成员:
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
void FunTest()
{
cout << "BFunTest()" << endl;
}
private:
int _b;
};
class Derived :public Base
{
public:
C()
{
cout << "C()" << endl;
}
void FunTest()
{
cout << "CFunTest()" << endl;
}
private:
int _c;
};
int main()
{
C c;
c.FunTest();
return 0;
}
试问这样访问的是基类的FunTest()函数还是派生累里的FunTest()函数呢?
答案是派生类里的
子类和父类中有同名 成员 , 子类成员 将屏蔽父类对成员 的直接访问。 ( 在子类成员函数中,可以
使用 基类: :基类成员 访问) –隐藏 –重定义
所以实际中在继承体系里面最好不要定义同名的成员 。
- 构造函数调用顺序
这里如果我们创建一个Derived对象 Derived c,那编译器是先调用基类的构造函数,还是派生类的构造函数呢?
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base" << endl;
}
void FunTest()
{
cout << "BFunTest()" << endl;
}
public:
int _b2;
protected:
int _b1;
private:
int _b;
};
class C :protected Base
{
public:
C()
{
cout << "C()" << endl;
}
~C()
{
cout << "~C()" << endl;
}
void FunTest()
{
_b1 = 0;
_b2 = 0;
cout << "CFunTest()" << endl;
}
public:
int _c2;
protected:
int _c1;
private:
int _c;
};
void FunTest()
{
C c;
}
int main()
{
FunTest();
system("pause");
return 0;
}
结果图:
这样看 是先调用基类的构造函数然后再调用派生类的,可是真的是这样吗?
我们来看看反汇编
F11进去发现
再F11
从这可以看到当我们创建一个派生类对象的时,首先调用的是派生类自己的构造函数,然后再调用基类的构造函数,刚才那个是因为代码执行的时候派生类函数里的输出函数在Base输出函数之后执行的。
所以我们一个在派生类构造函数的初始化列表位置显示调用基类
析构函数没有问题,先调用谁的构造函数就先析构谁。
总结:
另外要记得
1、 基类没有缺省构造函数, 派生类必须要在初始化列表中显式给出基类名 和参数列表。
2、 基类没有定义构造函数, 则派生类也可以不用定义, 全部使用缺省构造函数。
3、 基类定义了 带有形参表构造函数, 派生类就一定定义构造函数
- 继承赋值规则
- 就是父=子,不能 子=父.
父类指针引用可以子类而指针引用不能指向父类.(不过可以强制类型转换)
有一个典型的话:
你长的真像你爸,而不能说你爸长的真像你。友元关系与继承
静态成员与继承
如果在基类中定义一个静态,则整个继承体中有且仅有这个成员。单继承、多继承、菱形继承
- 单继承:
就相当于这个家庭都是独生子女,一个子类只有父类
- 多继承:
一个子类有多个父类。
- 菱形继承:
这里菱形继承会发现一个问题
- 单继承:
例如:
class A
{
public:
int _a;
};
class B :public A
{
protected:
int _b;
};
class C :public A
{
protected:
int _c;
};
class D :public B,public C
{
protected:
int _d;
};
void FunTest()
{
D d;
d._a;
}
int main()
{
FunTest();
system("pause");
return 0;
}
编译器会报一个错误
D的中有两份A的成员这是菱形继承存在的和冗余的问题。
这个问题用虚继承可以解决,所以之后再分析虚继承是如何做到的