接口 (抽象类)
接口可以用来描述一个 C++ 类的行为或功能,但是并不需要对这个类进行实现。
一个抽象类的声明里至少要有一个函数作为纯虚函数。在函数形参表后面写上 “= 0” 以指定纯虚函数:
建立抽象类 (通常被称为一个ABC) 的目的是提供一个适当的并且其他类可以继承的基类。抽象类不能实例化对象并且只能作为一个接口使用。试图实例化一个抽象类的对象会导致编译错误。
数据封装
数据封装是一种将数据和使用数据的函数结合在一起的机制;数据抽象是一种只将接口公开并且向用户隐藏实现细节的机制。
C++ 支持封装的属性和通过创建用户定义类型实现的数据隐藏,这称为类。我们已经研究过,一个类可以包含私有、保护和公有成员。默认情况下,所有定义在一个类中的成员是私有的。
经过一段痛苦的经历,我们大多数人已经学会了使类成员在默认情况下是私有的,除非我们真的需要使它们变成公有的。这就是一个好的的封装。
数据抽象
数据抽象的好处
数据抽象提供了两个重要的优势:
避免内部出现无意的,可能破坏对象状态的用户级错误。
随着时间的推移类实现可能会根据需求或缺陷报告来做出修改,但是这种修改无需改变用户级代码。
通过只在类的私有部分定义数据成员,类作者可以自由的对数据进行更改。如果实现更改,只需要检查类的代码看看这个改变可能造成什么影响。如果数据是公开的,那么任何可以直接访问旧的数据成员的函数都可能遭到破坏。
在 C++ 中,我们使用访问标号定义抽象接口类。一个类可以包含零个或多个访问标签:
成员定义了一个公有标号,程序的所有部分都可以访问这个公共标号。类型的数据抽象视图由其公有成员定义。
使用类的代码不可以访问带有私有标号的成员。对于使用类的代码,私有部分隐藏了类的实现细节。
一个访问标号可以出现的次数通常是没有限制的。每个访问标号指定了随后的成员定义的访问级别。这个指定的访问级别持续有效,知道遇到下一个访问标号或看到类定义提的右花括号为止。
类与对象的细节
内容 描述
类成员函数 类的成员函数是一个函数,像其他变量一样,成员函数在类中有其定义和原型。
类的访问修饰符 一个类成员可以被定义为公共,私有或保护。默认情况下成员将被假定为私有。
构造函数和析构函数 一个类的构造函数是一种特殊的函数,在创建一个类的新对象时调用它。析构函数也是一个特殊的函数,当创建对象被删除时调用它。
C++ 拷贝构造函数 拷贝构造函数是一个构造函数,它创建一个对象并用之前已经创建好的一个同类的对象对其进行初始化。
C++ 友函数 一个友(friend)函数允许完全访问类的私有成员和保护成员。
C++ 内联函数 使用一个内联函数,编译器试图用函数体中的代码替换调用函数的地方的函数名,从而达到消除函数调用时的时间开销的目的。
C++ 中的 this 指针 每个对象都有一个特殊的指针 this,它指向对象本身。
指向 C++ 类的指针 类指针和一个指向结构的指针是以完全相同的方式实现的。事实上一个类就是一个在其中包含了函数的结构体。
类的静态成员 类的数据成员和函数成员都可以被声明为静态的。
模板
模板是泛型编程的基础。泛型编程就是以独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。
使用模板的概念开发的库容器,像迭代器和算法都是泛型编程的例子。
每个容器都有一个单一定义,例如 vector,但我们也可以定义许多不同类型的 vector,如 vector 或 vector 。
你也可以使用模板定义函数和类,让我们看看是怎么做的:
template <class type> ret-type func-name(parameter list)
{
// body of function
}
这里的 type 是函数使用的数据类型的占位符名称。 这个名称可以在函数定义内使用。
下面是一个返回两个值中的最大值的函数模板例子:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
类模板
就像我们可以定义函数模板一样,我们也可以定义类模板。
模板类定义的一般形式如下所示:
template <class type> class class-name {
}
这里的 type 是一个类型的占位符名称,当类实例化的时候,此类型会被指定。 你可以用一个逗号隔开的列表定义多个泛型数据类型。
以下是一个定义Stack<>类并实现泛型方法来压入和弹出堆栈元素的例子:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const;// return top element
bool empty() const{ // return true if empty.
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// append copy of passed element
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// remove last element
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// return copy of last element
return elems.back();
}
int main()
{
try {
Stack<int> intStack; // stack of ints
Stack<string> stringStack;// stack of strings
// manipulate int stack
intStack.push(7);
cout << intStack.top() <<endl;
// manipulate string stack
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}