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

一、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)打开infile
istream 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达到目的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值