把"Essential C++"读薄(一)

来源:互联网 发布:apache服务器下载64位 编辑:程序博客网 时间:2024/05/22 07:08

一、Basic C++ Programming

1. 变量初始化的两种方式

· assignment运算符(=)初始化:沿袭自C语言,如果对象属于内置类型或者可以单一值初始化,这就没问题。例:string sequence_name = “Fibonacci”;
· constructor initialization syntax 构造函数初始化语法:在对象需要多个初值的情况,如complex类。例:complex<double> purei(0, 7);

2. array和vector

//用已初始化的array作为vector的初值const int seq_size = 18;int elem_vals[seq_size] = {    1, 2, 3, 3, 4, 7, 2, 5, 12    3, 6, 10, 4, 9, 16, 5, 12, 22};vector<int> elem_seq(elem_vals, elem_vals + seq_size);

3. 指针的使用

  一个没有指向任何对象的指针,地址为0(null指针),在使用指针前,必须在提领之前确定它的确指向某对象。为了防止对null指针进行提领,可以事先检验地址是否是0。if( pi && … )

4. rand()和srand()

#include <cstdlib>const int seq_cnt = 6;srand(seq_cnt);seq_index = rand() % seq_cnt;

  rand()和srand()都是标准库提供的伪随机数生成器,srand()的参数是所谓随机数生成器种子(seed),每次调用rand(),都会返回一个0和seq_cnt之间的一个整数,这里的值被限制在0~5。

5. 文件的读写

#include <fstream>//以输出模式开启seq_data.txt//如果指定文件不存在,会有一个文件被产生并打开供输出使用//如果文件已存在,这个文件被打开用于输出,而原有的数据会被丢弃ofstream outfile("seq_data.txt");//不希望丢弃原有内容,可增加一个参数,以append model打开文件//ofstream outfile("seq_data.txt", ios_base::app);if(!outfile)    cerr << "Oops! Unable to save session data!\n";else    //将数据写入    outfile << usr_name << ' ' << num_tires << ' ' << nun_right << endl;//以读取模式(input mode)打开infileistream infile("seq_data.txt");if(!infile) {...}else{    string name;    int nt, nc;    //这条与语句的返回值就是从infile中读到的class object    //读到文件末尾返回false    while(infile >> name)    {        //文件的形式:anna 24 19        //分别把两个整数读到nt和nc中        infile >> nt > nc;        if(name == usr_name) {...}    }}//同时读写同一个文件,为了以追加模式打开,传入第二参数fstream iofile("seq_data.txt", ios_base::in|ios_base::app);if(!iofile) {...}else    //开始读取之前,将文件重新定义至起始处    iofile.seekg(0);    //其他部分和上面的读取相同

  当我们以追加模式来打开文档,文件位置会位于末尾。如果我们没有先重新定位,就试着读取文件内容,那么立刻就会遇上“文件结束”的状况。seekg()可将iofile重新定位至文件的起始处。由于此文件是以追加模式开启,因此任何写入操作都会将数据添加在文件末尾。

二、 Procedural Programming

1. pass by value && pass by reference

  调用函数时,会在内存中建立一块特殊区域,就是程序堆栈(program stack),这个特殊区域提供了每个参数的存储空间,用pass by value的方式,使得对象在堆栈中复制了一份副本,对堆栈中的副本(形参)操作,一旦函数完成,这块堆栈内存就被释放掉,真正的自己(实参)没有发生任何改变。为了让形参和实参产生关联,要通过pass by reference的方式。
  以pass by reference方式将对象作为函数参数传入时,对象本身并不会复制出另一份,复制的是对象的地址。函数对该对象的任何操作,都相当于是对传入的对象进行间接操作。用pass by reference的方式传参,还可以降低复制大型对象的额外负担。
  但是注意下面情况,对根本不存在的对象进行寻址操作,是很不好的习惯。

vector<int> fibon_seq(int size){    vector<int> elems(size);    for(int ix = 0; ix < size; ++ix)        if(ix == 0 || ix == 1)            elems[ix] == 1;        else elems[ix] = elems[ix - 1] + elems[ix - 2];    return elems}

  这里以pointer或reference形式将elems返回,都不正确,因为elems在fibon_seq()执行完毕已不存在。如果将elems以传值方式返回,则不会产生问题,因为返回的是对象副本,它在函数之外仍然存在。如果返回的对象函数内的局部变量,就不能用传址的方式返回。除非我们用局部静态对象定义elems,如下:

vector<int>* fibon_seq(int size){    static vector<int> elems;    ...    return &elems;}

  局部静态对象所处的内存空间,即使在不同的函数调用过程中,依然持续存在,elems的内容不再当fibon_seq()每次被调用时就破坏又被重新建立。现在可以将elems的地址安全返回。

2. 动态内存管理

int *pia = new int[24];delete[] pia;

3. function overloading

  函数重载机制,参数列表不相同(可能是参数类型不同,可能是参数个数不同)的两个或多个函数,可以拥有相同的函数名称。注意编译器无法根据函数返回类型来区分两个具有相同名称的函数。

4. function template

template<typename T>void display_message(const string &msg, const vector<T> &vec){    cout << msg;    for(int ix = 0; ix < vec.size(); ++ix)    {        T t = vec[ix];        cout << t << ' ';    }}int main(){    vector<int> ivec;//编译器将T绑定为int类型    string msg;    display_message(msg, ivec);    vector<string> svec;//编译器将T绑定为string类型    //...    display_message(msg, svec);}

  一般而言,如果函数具备多种实现方式,我们可将它重载,其每份实例提供的是相同的通用服务。如果我们希望程序代码的主体不变,仅仅改变其中用到的数据类型,可以通果function template达到目的。

原创粉丝点击