C++ 三大特性之封装,继承,多态
1从C到C++在嵌入式的软件开发过程中大多是使用C这种面向过程的语言来开发,从效率上来说C语言已经非常高了,使用在硬件资源比较紧张的嵌入式系统来说是最好不过的选择了,随着硬件性能的提升,以及硬件价格的下降,C++逐渐应用于嵌入式系统中了,C++语言在编译器开优化的情况下,代码效率已经接近C语言了,并且提供了更高级的语言特性。(C语言也可以实现C++的特性LINUX内核就是使用了很多面向对象的...
1 从C到C++
在嵌入式的软件开发过程中大多是使用C这种面向过程的语言来开发,从效率上来说C语言已经非常高了,使用在硬件资源比较紧张的嵌入式系统来说是最好不过的选择了,随着硬件性能的提升,以及硬件价格的下降,C++逐渐应用于嵌入式系统中了,C++语言在编译器开优化的情况下,代码效率已经接近C语言了,并且提供了更高级的语言特性。(C语言也可以实现C++的特性LINUX内核就是使用了很多面向对象的思想)
2 封装(Encapsulation)
C语言也可以实现封装,使用struct关键字,把函数放进结构体中是从C到C++的根本改变(C语言使用函数指针)。将数据连同函数捆绑在一起的能力可以用于创建新的数据类型,这称之为封装(encapsulation),封装既是针对数据(属性)也是针对函数(行为)。
封装就是对现实世界的一种抽象。
事实上面向对象编程可以总结为一句话,“向对象发送消息”,实际上要做的所有事情就是创建一束对象并且给它们发送消息。
封装为什么好处呢?
- 内聚
- 增强安全性和简化编程
- 代码重用
隐藏实现:
在C语言中,struct与其它的数据结构一样,没有任何的规则,程序员可以struct中做他想做的任何事情,没有途径来强制任何的特殊行为。
C++语言中引入了三个新的关键字,用于在结构中设置访问的边界,public,private,protected。
- public
其后声明的所有成员可以被所有人访问。 - private
除去该类型的创建者和类的内部成员函数外任何人都不能访问。 - protected
在继承结构中可以访问protected成员,但是不能访问private成员。
在C++中使用class 关键字,它和C语言中的struct每一个方面都是一样的,除了class的成员默认是private的,而struct的成默认是public的。
如果一个函数被声明为friend时,就意味着它不这个类的成员函数,却可以修改该类的私有成员,且必须被列在该类的定义当中,这是一个特权函数,它突破了原有的访问控制权限。
3 继承(Inheritance)
C++中通过创建新的类来重用代码,而不是从头创建它们。继承是对抽象的一种升华。在编程实践中将公共特性部分抽出来放在父类里,了类则拥有特性化的有别于其它子类的特性。(is-a关系)
子类的访问权限:
继承方式 | 基类的public成员 | 基类的protected成员 | 基类的private成员 |
---|---|---|---|
public | 仍为public | 仍为protected | not access |
protected | 变为protected | 仍为protected | not access |
private | 变为private | 变为private | not access |
组合( composition)
组合是另外一种代码重用的方式。无论是组合还是继承都能把子对象放在新类型中。组合通常是在希望新类内部具有已存在类的功能时使用,而不是希望已经存在类作为它的接口,嵌入一个对象用以实现新类的功能,而新类的用户看到的是新定义的接口而不是来自老类的接口,为此在新类的内部嵌入已存在类的private对象。(has-a关系)
继承与静态成员
- 静态成员变量所有子类都只有一份
- 静态成员变量在类外初始化
- 一般使用静态成员函数访问静态成员变量
- 静态成员函数没有this指针,不是任何对象的组成部分。
4 多态 (polymorphism)
多态是通过虚函数实现的。
多态是运行时动态绑定。
动态绑定的条件:
- 第一:只有指定为虚函数的成员函数才能进行动态绑定
- 第二:必须通过基类类型的引用或指针进行函数调用
基类类型的引用或指针可以引用基类类型对象,也可以引用派生类型对象。
引用或指针的静态类型与动态类型可以不同,这是C++支持多态的基础。通过基类引用或指针调用基类中定义的函数时,并不能知道要执行的函数对象的确切类型,执行函数的对象有可能是基类类型也可能是派生类型的。
class Base
{
public:
virtual void func(){}
};
class DevidedA :public Base
{
public:
virtual void func(){}
};
class DevidedB :public Base
{
public:
virtual void func(){}
};
/
Base *b = new DevidedB ();
b->func(); //调用 DevidedB::func();
4.1 纯虚函数
函数定义为纯虚函数说明,此函数为后代类型提供了可以覆盖的接口,但是这个类中是不会调用,此外用户不能创建此类的对象。此类为抽象基类。
class Q_WIDGETS_EXPORT QAbstractButton : public QWidget
{
Q_OBJECT
....
protected:
virtual void paintEvent(QPaintEvent *e) = 0;
...
};
void QRadioButton::paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton opt;
initStyleOption(&opt);
p.drawControl(QStyle::CE_RadioButton, opt);
}
QAbstractButton 的所有派生类 QCheckBox, QPushButton, QRadioButton, QToolButton都要实现irtual void paintEvent(QPaintEvent *e)接口。
5 覆盖(Overriding)
- 覆盖与重写是一个意思
- 覆盖是借助于virtual关键字实现的
- 覆盖是子类覆盖父类
- 覆盖时子类与父类的函数名,形参,返回值是一样一样的。
摘自Qt源码的 focusOutEvent
void QAbstractButton::focusOutEvent(QFocusEvent *e)
{
Q_D(QAbstractButton);
if (e->reason() != Qt::PopupFocusReason)
d->down = false;
QWidget::focusOutEvent(e);//显式调用
}
在QAbstractButton的focusOutEvent方法里做特殊化的处理,再调用父类的focusOutEvent做通用化的处理。
6 重载(Overload)
静态的多态
6.1 函数
出现在相同的作用域中(同一个类中,同一个文件中)的两个函数如果具有相同的名字且形参表不同见为函数的重载(overload function)。编译器将根据所传递的实参类型来判断调用的是哪一个函数。函数不能仅仅基于不同的返回类型而实现重载。
错误的例子:
Record lookup(const Account&);
bool lookup(const Account&);
Record lookup(const Account&acct);
Record lookup(const Account&);
typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&);
Record lookup(Phone);
Record lookup(const Phone);
正确的例子:
Record lookup(Phone&);
Record lookup(const Phone&);
Record lookup(Name);
Record lookup(Address);
6.2 操作符
通过操作符的重载,程序能够针对类 类型的操作数定义不同的操作符版本。重载操作符是具有特殊名称的函数,保留字operator后接需要定义的操作符符号,像其它函数一样,重载操作符具有返回类型和形参列表。
格式:
Object operator + (const Objcet&,const Object&);
内置类型的操作符,不能重载(不能重载int类型的“+”操作符)
重载一元操作符如果作为成员函数就没有形参(显式),如果作为非成员函数就有一个形参,同样的,重载二元操作符定义的成员时有一个形参,定义为非成员函数时有两个形参。
操作符定义为非成员函数时通常必须将它们设置为所操作类的友元(friend)
7 重定义(Redefining)
当子类与基类的成员同名时将屏蔽对基类成员的直接访问。
7.1 成员变量
class Base
{
public:
Base():mem(0){}
protected:
int mem;
};
class Derived :public Base
{
public:
Derived():mem(0){} //初始化Derived::mem;
int getMem()
{
return mem;//返回 Derived::mem;
}
int getBaseMem()
{
return Base::mem;//返回 Base::mem;
}
protected:
int mem;
}
7.2 成员函数
在基类和子类中使用同一名字的成员函数,其行为与数据成一样:在子类作用域中子类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽。
class Base
{
public:
Base();
int func();
};
class Derived :public Base
{
public:
Derived();
int func(int);
}
Base b;
Derived d;
b.func();
d.func(2);
d.Base::func();
d.func(); //error
- 不同的作用域
- 若重新定义了基类中的一个重载函数,则在派生类中,基类中该名字函数(即其他所有重载版本)都会被自动隐藏,包括同名的虚函数
- 对函数的返回值、形参列表无要求
8 有用参考
《C++编程思想》
《C++ Primer》
https://blog.csdn.net/lms1008611/article/details/81515727 (图)
https://www.cnblogs.com/DannyShi/p/4593735.html
https://blog.csdn.net/haoel/article/details/1948051/
更多推荐
所有评论(0)