这篇文章上次修改于 2320 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

2 C++及其标准库简介

C++标准化从1989年开始到1997年底完成。1998年9月公布。

template (模板)

模板是针对一个或多个尚未明确的型别编写的函数或类别。使用template时可以显式地(explicitly)或隐式地(implicitly)将型别作为参数进行传递。

template <class T>
inline const T& max(const T&a, const T&b)
{
    //if a < b then use b else use a
    return a < b ? b: a;
}

Nontype Template (非型别模板参数)
型别(type)可以作为template 的参数,非型别(nontype) 也可以作为template 的参数。

bitset<32> flags32;
bitset<50> flags50;

Default Template Parameters(缺省模板参数)

template <class T ,class containter = vector<T> >
class MyClass;

MyClass<int> x1;
MyClass<int, vector<int> >

关键字typename

template <class T>
class MyClass 
{
    typename T::SubType *ptr;
};

Member Template(成员模板)
class member function 可以是个template,但这样的member template既不能是virtual 也不能有缺省参数

class MyClass 
{
    template <class T>
    void f(T);
};

Nested Template Classes
嵌套的(nested) classes 本身也可以是个template

template <class T>
class MyClass 
{
    template <class T2>
    class NestedClass;
};

基本型别的显式初始化(Explicit Initialization)

如果采用不含参数的,明确的constructor(构造函数)调用语法,基本型会被初始化为零。

    int i1;
    int i2 = int();

下面这个函数可以保证x被初始化为0

template <class T>
void f()
{
    T x = T();
};

异常处理(Exception Handing)

通过异常处理,c++标准库可以在不污染函数接口(亦即参数和返回值)的情况下处理异常。如果你遇到这样一个意外情况,可以通过抛出一个异常来停止一般的处理过程

class Error;
void f()
{
    if(exception-condition) 
    {
        throw Error();
    }
}

语句throw开始了stack unwinding(堆栈辗转开解)过程。

命名空间(Namespaces)

Namespace将不同的标识符集合在一个具名作用域(named scope)内,如果你在namespace之内定义所有标识符号,则namespace本身名称就成了唯一可能与其他全局符号冲突的标识符号。

namespace josuttis 
{
    class File;
    void myGlobalFunc();
}

josuttis::File obj;

使用using namespace josuttis;可以使namespace内的所有名字曝光。但是这样可能会出现命名冲突。

bool型别

为支持布尔值,c++增加了bool型别,bool可以增加程序的可读性,并允许你对布尔值实现重载操作。
false 0 , true 1;

关键字explicit

关键字explicit的作用,可以禁止"单参数构造函数(single argument constructor)"被用于自动类型转换。典型例子是群集类别(collection classed),你可以将初始长度作为参数传递给构造函数,利于可以声明一个构造函数,以stack的初始大小作为参数:

class Stack
{
    explicit Stack(int size);
};

这里的explicit的应用非常重要,如果没有,这个构造函数有能力将一个int自动转型为stack,一旦出现,你可以给stack赋值一个整数而不引起任何问题。
Stack s;
s = 40;
自动类型转换会把40转换为有40个元素的stack并指派给s,如果我们将构造函数声明为explicit,上述赋值操作就会导致编译错误。
explicit同样可以阻绝"以赋值语法进行带有转型操作的初始化"

Stack s1(40);
Stack s2(40:

这是因为以下两组操作:

X x;
Y y(x); //explicit conversion

X x;
Y  y = x; //implicit conversion

前者通过显式转换根据型别X产生了一个型别y的新对象,后者通过隐式转换,产生了一个型别Y的新对象。

新的型别转换操作符(Type Conversion Operators)

  1. static_cast 将一个值以符合逻辑的方式转型,这可以看作是“利用原值重建一个临时对象,并在设立初始值时使用类型转换”。唯一当上述的型别转换有所定义,这个转换才会成功。
float x;
cout << static_cast<int>(x);
f(static_cast<string>("hello"));
  1. dynamic_cast

将多态型别(polymorphic type)向下转型(downcast)为其实际静态型别(real static type).这是唯一在执行期进行检验的转型动作。

class Car;
class Cabriolet :public Car
{
    ...
};
class Limousine :public Car
{
    ..
};

void f(Car *p)
{
    Cabriolet *p = dynamic_cast<Cabriolet*>(cp);
    if(p == NULL) {
        //..did not refer to an object of type Cabriolet
    }
};
  1. const_cast

设定或去除型别的常数性(constness) 亦可以去除volatile修饰,除此之外不允许任何转换

  1. reinterpret_cast

此操作符的行为由编译器进行定义,可以重新解释bits意义,但也不一定如此,使用该转型动作统称会带来不可移植行。

常数静态成员(constant static members)的初始化

如今我们中午能够在class中对“整数型(integral)常数静态成员”直接赋予初值了。

const MyClass
{
    static const int num =100;
    int elems[num];
};

const int MyClass::num;

main()定义式

根据c++标准,只有两种main()是可移植的

int main()
{}

int main(int argc, char* argv[])
{}

Big-O表示法

常数 Q(1) 运行时间与元素个数无关
对数 O(log(n)) 运行时间随元素个数的增加呈对数增长
线性 O(n) 运行时间随元素个数的增加呈线性增长
n-log-n O(n*log(n)) 运行时间随元素个数的增加呈线性和对数的乘积增长
二次 O(n^2) 运行时间随元素个数的增加呈平方增长