前言:C++最初诞生时,仅仅被称作带类的C。这说明C和C++是一衣带水的关系,随后C++扩充了很多特性,成为了面向对象的一个语言,真正成了扛把子。那么C++有什么优势呢?举个例子类比,一个C代码项目就像是一个冰柜,所有的东西都装在一块,且不说会不会“窜味儿”,看起来很没条理。而C++就像是一个冰箱,有冷藏室冷冻室,冷藏室还有放鸡蛋的地方,放蔬菜的地方...当然这只是其中最早的一个好处:封装性。但是C语言没法写出C++的特性,这就依赖于下面要介绍的C++的其他特性。
一. C++ beyond C
C++扩展了一些新的特性,其中最主要的包括以下几个:
- 封装(类)
- 继承
- 多态
- STL
接下来会简单介绍一下每一个特性,最后看一下STL的主要的容器。
二. 特性介绍
2.1 封装(类)
通过抽象,类把一类东西的属性和方法封装到了一起。这就是美其名曰“面向对象”的概念。就是代码的组织不再是流程控制,而是对象的组织。C语言的流程控制是通过判断条件来跳转执行的,而C++对象的组织则是通过继承,多态等方式组织的。
class Test
{
public:
int x_;
void init(int x, int y,int z);
void display()
{
cout<< x_ << endl;
};
private:
int y_;
protected:
int z_;
};
上面是一个类的典型定义,从中可以看出把数据和函数封装到了一块,并通过关键字public,private,protected来限制访问权限。
从上面的定义看,跟C中的结构体也可以实现类似的功能,但是C语言没法设置访问权限,也有一些人喜欢把C写成C++风格的。
2.2 继承
我们上面提到过,C语言是组织流程,所以,代码中到处都是条件判断和流程处理。但是在C++中,要组织的却是一个个对象。要先抽象,定义出对象的雏形。然而,还有一个问题必须解决,C++面向的是对象,可世间对象千千万,大千世界最不缺的就是差异,那么千千万不同的种类以及实例化的对象该怎么管理呢?答案就是继承和多态。继承解决相同的部分,多态解决差异的部分。这一节就先介绍继承。
继承使得新的类不必完全重新定义,而是借助于已有的类定义,这大大的减轻了创建管理类的工作。继承可以使得新类从基类中获取属性和方法(public继承时)。
class base{
public:
void cacl(int n) {printf("good");}
private:
int i,j;
};
class next : public base{
public:
void mix(int m) {printf("well");}
};
关于创建新的类时是否需要采用继承的办法,需要注意:如果新类时基类的一种,如,哈士奇是狗的一种,哈士奇还有其他一些属性和方法,如卖萌;这个时候,哈士奇类可以继承狗这个类。另一种情况是,眼睛是头的一部分,但不能说眼睛是基类,头是派生类,因为头显然没有看东西这个功能。这个时候,创建头这个类时,应该在头这个类种包含眼睛这个类,而不是继承。
class head{
public:
void cacl(void);
class eye eyes;
private:
int len;
};
2.3 多态
好了,要创建新类,先抽象找共性,通过继承新的类建立起来了,然而,很快就会遇到一个问题:类的行为千差万别,即使对于同一个方法也会有不同的表现,如对于吃饭,印度人是手抓着吃,中国人是用筷子,欧美人是用刀叉。这种情况下,如果定义一个基类,要不要定义吃饭这个方法呢?如果不定义,那么各个派生类类就得定义自己的吃饭方法,更要命的是,一个类和另一个类可能不光吃饭不相同,几乎所有的方法实现起来都不相同...比如,睡觉,休闲,运动都不相同。那么各个派生类都去定义自己的一套,继承不就成了摆设了么?说好的代码重用呢?
这就是多态的用处,多态可以实现对于每个派生的类,当调用同一个方法时,实现不同的行为,即调用到自己的方法实现。C++的多态可以分为两种:
- 静态多态
- 动态多态
所谓的静态多态就是通过函数重载的办法实现的,重载可以针对操作符也可以针对函数。用重载时,函数名字相同,参数和返回值不同,实际上是在编译阶段根据参数的类型来确定使用哪个函数。
而动态多态是通过虚函数的方式实现的,也就是在基类中先定义虚函数,然后在派生类中定义自己的方法,然后覆盖基类的方法。
#include<iostream>
using namespace std;
class Base
{
public:
virtual Base* FunTest()
{
cout << "victory" << endl;
return this;
}
};
class Derived :public Base
{
public:
virtual Derived* FunTest()
{
cout << "yeah" << endl;
return this;
}
};
int main()
{
Base b;
Derived d;
b.FunTest();
d.FunTest();
return 0;
}
2.4 STL模版库
为了提高C++的开发效率,对代码能够做到重用,STL诞生了,它一开始并不是C++的一部分,STL实际上提供了泛型编程在C++上的实现。STL考虑的事情是:对于一个排序功能,无论使用哪种数据结构,数组也好,链表也好;无论哪种数据类型,int也好,float也好,都应该能够实现排序功能。为了这个目标,STL在实现时,划分成了三个部分实现
- 容器
- 算法
- 迭代器
容器提供了常用的数据结构,set,map,list,vector等;算法提供了排序等几十种算法;而迭代器呢?迭代器实际上就是要匹配容器和算法,根据不同的容器元素类型,采用对应的排序实现。如,数组排序和链表排序。
那么再回过头来看,STL是标准模版库的简写。模版是什么?我们前面说到过STL是为了提供泛型支持,回想一下会发现,函数重载是不是也解决了部分问题?(函数重载通过实现多个参数不同的原型,也能提供部分泛型支持)。但是,一直重载函数也不是一个有效的办法来解决泛型支持问题,所以,模版诞生了。当然,这里一直没有说类模版,只提到了函数模版。模版的典型定义如下:
template<typename(或class) T>
T fuc(T x, T y)
{
T x;
//……
}
接下来简单介绍一下STL提供的几种容器:
2.4.1 几种常用容器
c++中有两种类型的容器:顺序容器和关联容器,顺序容器主要有:vector、list、deque等。其中vector表示一段连续的内存地址,基于数组的实现,list表示非连续的内存,基于链表实现。deque与vector类似,但是对于首元素提供删除和插入的双向支持。关联容器主要有map和set。map是key-value形式的,set是单值。
vector - 需要包含
vector提供的操作主要有:#include<vector>
- vec1.push_back(100);
- vec1.size();
- vec1.empty();
- vec1.pop_back();
- vec1.insert(vec1.end(),5,3);
- vector::const_iterator c_iter = vec1.begin();
list - 需要包含
list是一个双向链表,提供的主要操作是:#include<list>
- lst1.push_back(10);
- lst1.pop_back();
- lst1.sort();
- lst1.reverse();
- lst1.remove(2);
map - 需要包含头文件
map提供了一种存放#include<map>
<key,value>
对儿的结构,并自动排序。主要的操作是:- map1.insert(make_pair<int,string>(4,"V5"));
- map1.erase(3);
- map1.size();
三. 总结
C++提供了面向对象的设计方法,很多特性都是围绕着类和对象的创建,管理,使用展开的。本文只简单介绍了C++中的一些关键东西,还有更多细节的东西,如构造函数,析构函数,智能指针等暂时略去不表。但是只要掌握了C++的核心内容,细节使用随着经验积累,自然会心中有天地。