C++学习之初步

来源:互联网 发布:网络音乐游戏排行榜 编辑:程序博客网 时间:2024/06/03 02:27

本人学习C++有段时间了,但是对C++还是有些部分不是很了解。因此专门找了本讲解C++的书籍,边看边总结,希望能增加对C++的理解。该书的名字是《C++面向对象程序设计》,作者李晋江,刘培强。选择该书的原因是:它是一本面向教学的书籍,里面的知识讲解都很清晰明了。本人打算先增加下对C++的理解,再去拜读下那些权威名著。

第一部分:C++语言初步

1.    命名空间

1). C++中的命名空间是用于避免类名的冲突,尤其会在大型的项目中体现出重要的作用。C++采用的是单一的全局变量命名空间。命名空间的定义:

namespace mynamespace //命名空间名字,可以自己定义,但不能与其他命名空间重名

{

           int i;

           classmyclass //以上是内部定义方法

{

 

};

void func1();

…….

}

当然也可以选择外部定义的方法,但必须要先在命名空间内部声明,例如上面的func()函数,在命名空间内只对其做了声明而没有定义。因此

void mynamespace::func1() {i++;} //选择外部定义的方式

 

如果要为已定义的命名空间增加成员应该怎么办呢?因为命名空间是开放的,因此命名空间可以通过多次声明和定义同一命名空间,每次添加自己的新成员和名称来实现。如:

namespace mynamespace

{

           int j;

           voidfunc2();

}

切记:不可对std的命名空间增加或删除任何成员。

 

2).如果使用命名空间?

命名空间有三种使用方法:(1)usingnamespace mynamespace; 即using namespace 加上命名空间的名字。(2)using mynamespace::member; 即using 加上命名空间的名字::成员。(3)直接在使用命名空间内的成员时加上该命名空间的名字。如我们在使用cout时可以用std::cout表示。上面三种方法在使用的时候各有优缺点,第一种方法可以减少编程人员的输入,但有可能会引起命名冲突;第三种方法需要很多的输入;第二种方法是以上两种方法的折中,但如果使用不当也可能会造成冲突。

需要注意的几点:

命名空间可以是没有名字的:如

namespace

{

           members;

}

其作用是将成员局限于一个特定的文件,不能跨越多个文本文件。

 

可以为命名空间取个别名:如

namespace mynamespace = mns;

 

不同的命名空间可以进行组合和选择;如果不同命名空间使用在同一程序中,他们间的函数可以实现重载。

 

2.    输入和输出

C++中实现流简单输入和输出的是iostream 对象cin和cout;而且cin和cout也是比较熟悉的。同C语言提供了格式化输入输出的方法,C++也提供了输入输出的控制符。

首先先说说cout

cout做为一个iostream对象,可以使用setf()/unsetf()或flags()成员函数设置格式。

其常用的输出格式包括以下几种:

ios::dec   十进制输出

ios::hex   十六进制输出

ios::oct   八进制输出

ios::showbase 添加一个表示其进制的前缀

ios::internal   使输出字符串两端对齐

ios::left      左对齐

ios::right     右对齐

ios::boolalpha 将bool类型的值以true/false输出

ios::fixed     将浮点数按照普通定点格式处理

ios::scientific 将浮点数按照科学计数法处理

 

在头文件<iomanip>头文件中也包括一些输入输出的控制符,常用的包括:

setw(int num); 用当前的填充字符控制对齐位置,默认的填充字符是空格

setfill(char ch); 设置填充字符

setprecision(int num); 设置输出精度

 

在输出流中有一个函数endl,该函数等价于<<”\n”<<flush,即先输出一个换行符,再实现缓冲区的刷新。flush是刷新流,在C语言中用ffush(FILE* stream)实现。

同时介绍一个打开和关闭缓冲的机制:setbuf(FILE *stream, char *buf);把缓冲区与流相关联。

#include<iostream>

#include<iomanip>

#include<windows.h>

using namespace std;

int main()

{

    char* pbuffer = new char[1024];

    setbuf(stdout,pbuffer);

    cout<<"hello world";

    Sleep(1000);

    cout<<"hello world";

    Sleep(1000);

    cout<<"helloworld"<<flush;

    delete pbuffer;

    return 0;

}

该例程的输出结果是:

hellow worldhello worldhello world

 

其次介绍下cin输入。

下面主要介绍下cin的一些成员函数,cin.get() ; cin.getline(); cin.clear(), cin.sync(); cin.rdstate()。

cin.get()是一个读取单个字符的方法。

cin.getline(char *line, int size, char ch);获取一整行文本,第三个参数是结束标识符

然后cin.rdstate()使用来检测输入错误的,它可以返回四种结果:

ios::goodbit  流状态正常

ios::badbit   流出现致命错误,不可挽回

ios::eofbit    已经到达文件尾

ios::failbit    流出现非致命错误,可挽回

当流出现非致命错误,即ios::faibit时可以通过cin.clear()//  清除错误位和cin.syc()//清空流。来操作实现流的重新输入。

 

3.    string类型

string类型包含在头文件<stirng>中,这里要注意是<string >而不是<string.h>;后者是C语言中的字符串头文件。

string类型可以理解为是C语言中字符串数组的进化版本。string类具有多个成员函数,因此它可以实现不同的操作。

 

 

首先是初始化一个string类型:string类型有多种构造函数,以及复制构造函数和copy assignment 操作符。

如:

string str; default constructor

str = “Hellow world”; copy assignment operator

char cstr[] = “abcde”; C 字符串

string s1(str); // copy constructor

string s2(str, 6);// 将str内,开始位于位置6的部分当做s2的初值

string s3(str, 6,3);//将str内,开始位于位置6且长度最多为3的部分作为s3的初值

string s4(cstr);

string s5(cstr, 3);// 将C字符串前3个字符作为字符串s5的初值

string s6(5, ‘A’); //生成一个字符串,包含5个’A’字符

需要注意的是:

不可以提供以字符和正数为参数的构造函数,即

string error1 = ‘C’; // error

string error2(‘u’); // error

但是可以先定义str对象,然后用字符对其赋值,如

string str ;

str = ‘A’;//这样是正确的。

 

string类型的比较等操作。C++string类型对==,>, <, >=, <= 和!= 以及 + , +=, []操作符都做了重载,可以实现string类型数据的比较。其中[]操作符,可以用成员函数at()替代。而且str.at(i)提供了范围检查,当越界时会抛出out_of_range异常。当然遍历string类型数据也可以用迭代器iterator;而且str.begin() str.end()和str.rbegin() str.redn()分别实现了string数据类型的正反方向的遍历。

 

string类还提供了一些其它的成员函数,比如特性描述,查找等方面的成员函数

         1).string特性描述

         intcapacity() const;// 返回当前容量

         intmax_size() const;// 返回string对象中可存放的最大字符串的长度

         intsize() const;// 返回当前字符串的大小

         intlength() const; // 返回当前字符串的长度

         boolempty() const;// 当前字符串是否为空

         voidresize(int len, char c);// 把字符串当前大小设置为len,多去少补,并由字符c填充//多出的部分

 

         2). string的查找

         size_typefind (const basic_string &str, size_type index);

         //返回str在字符串中第一次出现的位置(从index开始查找),否则返回string::npos

         size_typefind (const char*str, size_type index); // 同上

         size_typefind(const char*str, size_type index, size_type length);

         //返回str在字符串中第一次出现的位置(从index开始查找,长度为length),否则返回//string::npos

         size_typefind(char ch, size_type index);//查找字符在字符串第一次出现的位置

 

         3). 其它常用函数

         string& insert(int p, const string &s);

         string&replace(int p, int n, const char *s);//p为起始位置,n为长度(下同)

         string&erase(int p, int n);

         stringsubstring(int pos =0, int n = npos) const;

         voidswap(string &s2);

         string&append(const char *s);

         voidpush_back(char c);

         constchar* data() const;// 返回一个非NULL终止的c字符串

         constchar* c_str() const;// 返回一个NULL终止的c字符串

以上函数从函数名就可以判断出它们的作用。

 

4.    new和delete

new和delete 用于内存的动态管理。其与C语言的malloc和free有一定的区别。虽然它们都是用于内存的动态管理。但是malloc和free是C语言中的函数,new和delete是操作符。因此它们在使用的时候也会有不同的地方。典型的是对非内部类型数据的内从的动态管理。malloc和free是适合于内部数据类型的分配和销毁。但是对于非内部的数据类型,比如一个类,malloc函数只会分配一定的内存空间,但不会调用类的构造函数,free也不会调用类的析构函数。而new和delete作为运算符,可以用于重载,它是可以实现类在动态分配时调用它的构造和析构函数。(可以编写一个类做验证)

 

需要注意的是malloc和new在分配内存的时候会产生失败。其中malloc产生失败是会返回NULL的指针值;而new则有两种情况:1).默认情况下会抛出异常;2).但也可以include头文件<new>,其中包含了一个不抛出异常的new,即T *p = new(nothrow())T[MAX_SIZE]。综上所述在使用malloc和new时要对内存是否分配成功进行判断。

对于malloc和不抛出异常的new的判断方法是一样的:

#include <new>

int *p_int = new(nothrow)int [len];

if(NULL == p_int)

if(NULL == p)

{

           cout<<”memory allocate error!”<<endl;

           return -1;

}

 

 

char *p = (char *)malloc(len*sizeof(char));

if(NULL == p)

{

           cout<<”memory allocate error!”<<endl;

           return -1;

}

而对于抛出异常的new,要做异常的捕捉。

try

{

           int *p_int = new int[len];

}

catch(const bad_alloc&e)

{

           return -1;

}

 

接着简述下栈和堆的区别:

栈,就是由编译器在需要的时候分配的,里面的变量通常是局部变量,函数参数等。

堆:就是由malloc或new分配的内存块,它的释放不是由编译器释放的,而是应该由free和delete去释放的。

两者的区别可以总结为一下几点:

1)       管理方式和碎片问题: 栈是由编译器管理的,它是一个先进后出的队列,是不会产生碎片的。而堆是由程序动态的分配和释放内存,频繁的分配和释放内存势必会造成内存空间的不连续,从而产生大量碎片。

2)      分配效率:栈是系统提供的数据结构,计算机为其分配了专门的寄存器存放栈地址,因此它的效率是比较高的。而堆则是C/C++提供的一种内存机制,它的效率比栈低。

3)      增长方式:栈的内存地址的增长方式是从高地纸到低地址增长的,开辟栈时,栈指针向低地址方向增长,回收栈时,栈指针向高地纸方向移动。而堆来讲,它的增长方式是向上的,分配时向高地址移动,回收时向低地址移动。对于32位系统来说,堆可以达到4GB的内存空间,而栈一般都是有一定的空间大小的。

原创粉丝点击