面向对象三大机制包括:
1.封装,隐藏内部实现。
2.继承,复用现有代码。(面向对象最显著的特征)
3.多态,改写对象行为。
继承,是一种复用的手段。是从先辈处得到属性和行为特征。类的继承就是新的类从已
有的类那里得到已有的特征。
类成员的访问限定有三种:public,protected,private,在类外可以访问类的公有成员,
私有和保护都不可以访问。代码举例:
#include<iostream>
using namespace std;
class A
{
public:
A()
:_pub(1)
,_pro(2)
,_pri(3)
{}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
int main()
{
A a;
cout << a._pub << endl;
cout << a._pro << endl;
cout << a._pri << endl;
system("pause");
return 0;
}
代码编译不通过,提示不可访问类的私有和保护成员。
也有3种继承关系:public(公有继承),protected(保护继承),private(私有继承)
那么父类成员在子类中的访问属性究竟会是怎样的呢?
【总结】在继承机制下,无论哪种继承方式,子类中可以访问父类的公有保护成员,
私有成员不可访问;在类外,只能访问公有成员,私有和保护都不可访问。这个可以通
过代码去验证。
类外只能访问类的公有成员。如果一些父类成员不想被父类对象直接访问,但是需
要在派生类中访问,就定义成保护成员。protected成员的特点:“外人”不可访问,“儿
子”可以访问。
class中继承方式默认是private,struct中默认是public。
类与类之间的关系有三种:
1.has-a,一个类的成员是另一个类的对象;私有继承是另一种实现has-a的途径。
2.uses-a,一个类的成员函数的参数是另一个类的对象
3.is-a,公有继承,父类可用的成员对子类也是可用的。
赋值兼容规则:
前提:public继承下。
1.父类对象 = 子类对象
举例:人通过继承得到超人,即超人具备人的特性,当然也有其本身特有的功能。
人 = 超人(超人的特异功能不被赋值)
2.父类对象不可赋值给子类对象。
3.父类指针可以指向子类对象,如图。子类指针不可以指向父类对象。但是可以通过
强制类型转换完成。但是也可能会出现一定的问题。(下边的代码)
4.父类对象的引用可以引用子类对象。(与指针同理)
class B
{
public:
B()
:_pub(10)
, _pro(20)
, _pri(30)
{
cout << "B()" << endl;
}
void show()
{
cout << _pub << endl;
cout << _pro << endl;
cout << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class D:public B
{
public:
D()
:B()
,_i(100)
{
cout << "D()" << endl;
}
void show(int )
{
cout << _pub << endl;
cout << _pro << endl;
//cout << _pri << endl;
}
int _i;
};
int main()
{
D d;
B b;
D *pd;
pd = (D *)&b;//子类对象通过强转指向父类对象
pd->_i = 10;
system("pause");
return 0;
}
运行之后会出现程序崩溃。所以尽量不要强转。
注:创建子类对象时,会先调用父类的构造函数,然后再调用子类的构造函数,结束
时,先调用子类的析构函数,再调用父类的析构函数。若父类没有默认的构造函数,子
类构造函数中对父类成员的初始化必须在初始化列表中实现。
如果子类中有与父类同名的函数或者成员,父类的函数或者成员被隐藏,与函数的参
数,返回值类型都无关。隐藏,父类的函数仍然存在。
举例:
class B
{
public:
B()
:_b(1)
{}
void Show()
{
cout << _b << endl;
}
private:
int _b;
};
class D:public B
{
public:
D()
:B()
,_b(2)
,_d(3)
{}
void Show()
{
cout << _b << endl;
cout << _d << endl;
}
private:
int _b;
int _d;
};
int main()
{
B b;
D d;
b.Show();
d.Show();
cout << sizeof(b) << endl;
cout << sizeof(d) << endl;
system("pause");
return 0;
}
D中有与B同名的成员和函数,所以子类中就会隐藏父类的成员,但是父类的成员依然存
在,b.Show()依然可以打印出B中的成员。b的大小是4字节,d的大小是12字节。这就是
所谓的隐藏。
多重继承:
前边整理的都是都是单继承,即一个类从一个父类派生而来。当一个子类有多个父类
时,这种继承方式成为多重继承。
在多重继承中,最典型的继承方式就是菱形继承。
为了解决菱形继承中出现的问题,我们引入了虚基类。看下边的代码:
class A
{
public:
A()
:_a(1)
{}
protected:
int _a;
};
class B :virtual public A
{
public:
B()
:_b(2)
{}
protected:
int _b;
};
class C :virtual public A
{
public:
C()
:_c(3)
{}
protected:
int _c;
};
class D :public B, public C
{
public:
D()
:_d(4)
{}
protected:
int _d;
};
int main()
{
D d;
cout << sizeof(d) << endl;
system("pause");
return 0;
}
要是没有在B,C的派生列表中加上virtual关键字,最终输出d的大小是20字节,加上之
后呢就是24字节。
我们知道,如果是单继承的话,子类成员在内存中的情况就是:先是来自父类的成员,
然后就是来自子类的成员,紧挨存储。那么虚继承的对象模型究竟是什么,打开内存看
一下:(windows下的情况)linux下的情况之后补充 。
大家也可以打开内存看单继承和菱形继承的对象模型,这里就不给出了。
友元与继承:
由于友元不可以传递,所以友元就不能继承。父类的友元不可以访问子类的私有和保
护成员,但是可以访问子类从父类继承下来的成员。父类的友元要想访问子类,必须将
该友元声明为子类的友元。
class D;
class B
{
friend void Show(B &b,D &d);
public:
B()
:_b(1)
{}
protected:
int _b;
};
class D:public B
{
public:
D()
:_d(4)
,_e(5)
,_f(6)
{}
public:
int _d;
protected:
int _e;
private:
int _f;
};
void Show(B &b, D &d)
{
cout << b._b << endl;
cout << d._b << endl;
cout << d._d << endl;
//cout << d._e << endl;
//cout << d._f << endl;
}
int main()
{
B b;
D d;
Show(b,d);
system("pause");
return 0;
}
父类的友元函数并不能访问子类的私有成员和保护成员。
静态成员与继承:
如果父类定义了一个静态成员,则在整个继承体系中都只有该成员的唯一定义,不管
从父类派生出多少子类,对于每个静态成员来说只存在唯一的实例。并且子类的对象都
能访问父类的静态成员。这里就不给出代码了,感兴趣的可以自己写出代码。
以上问题如有不合理的地方,希望联系1521107105@qq.com,不胜感激~