关于c++的一些注意事项
2007-03-17 19:41 OntheMars 阅读(219) 评论(0) 收藏 举报jiajie999搜集整理于互联网,感谢原作者
vc里的int64
声明 __int64 cin/cout不支持__int64 用 scanf("%I64d",&a); printf("%I64d",a);
声明 unsigned__int64 cin/cout不支持__int64 用 scanf("%I64u",&a); printf("%I64u",a);
dev_c++里的int64
声明 longlong cin/cout 支持__int64
声明 unsigned longlong cin/cout 支持__int64
声明一个字符串变量很简单:
string Str;
这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string的默认的构造函数,这个函数所作的就是把Str初始化为一个空字符串。String类的构造函数和析构函数如下:
a) string s; //生成一个空字符串s
b) string s(str) //拷贝构造函数 生成str的复制品
c) string s(str,stridx) //将字符串str内“始于位置stridx”的部分当作字符串的初值
d) string s(str,stridx,strlen) //将字符串str内“始于stridx且长度顶多strlen”的部分作为字符串的初值
e) string s(cstr) //将C字符串作为s的初值
f) string s(chars,chars_len) //将C字符串前chars_len个字符作为字符串s的初值。
g) string s(num,c) //生成一个字符串,包含num个c字符
h) string s(beg,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值
i) s.~string() //销毁所有字符,释放内存
都很简单,我就不解释了。
2.字符串操作函数
这里是C++字符串的重点,我先把各种操作函数罗列出来,不喜欢把所有函数都看完的人可以在这里找自己喜欢的函数,再到后面看他的详细解释。
a) =,assign() //赋以新值
b) swap() //交换两个字符串的内容
c) +=,append(),push_back() //在尾部添加字符
d) insert() //插入字符
e) erase() //删除字符
f) clear() //删除全部字符
g) replace() //替换字符
h) + //串联字符串
i) ==,!=,<,<=,>,>=,compare() //比较字符串
j) size(),length() //返回字符数量
k) max_size() //返回字符的可能最大个数
l) empty() //判断字符串是否为空
m) capacity() //返回重新分配之前的字符容量
n) reserve() //保留一定量内存以容纳一定数量的字符
o) [ ], at() //存取单一字符
p) >>,getline() //从stream读取某值
q) << //将谋值写入stream
r) copy() //将某值赋值为一个C_string
s) c_str() //将内容以C_string返回
t) data() //将内容以字符数组形式返回
u) substr() //返回某个子字符串
v)查找函数
w)begin() end() //提供类似STL的迭代器支持
x) rbegin() rend() //逆向迭代器
y) get_allocator() //返回配置器
更改内容
这在字符串的操作中占了很大一部分。
首先讲赋值,第一个赋值方法当然是使用操作符=,新值可以是string(如:s=ns) 、c_string(如:s=”gaint”)甚至单一字符(如:s=’j’)。还可以使用成员函数assign(),这个成员函数可以使你更灵活的对字符串赋值。还是举例说明吧:
s.assign(str); //不说
s.assign(str,1,3);//如果str是”iamangel” 就是把”ama”赋给字符串
s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s
s.assign(“gaint”); //不说
s.assign(“nico”,5);//把’n’ ‘I’ ‘c’ ‘o’ ‘/0’赋给字符串
s.assign(5,’x’);//把五个x赋给字符串
把字符串清空的方法有三个:s=””;s.clear();s.erase();(我越来越觉得举例比说话让别人容易懂!)。
string提供了很多函数用于插入(insert)、删除(erase)、替换(replace)、增加字符。
先说增加字符(这里说的增加是在尾巴上),函数有 +=、append()、push_back()。举例如下:
s+=str;//加个字符串
s+=”my name is jiayp”;//加个C字符串
s+=’a’;//加个字符
s.append(str);
s.append(str,1,3);//不解释了 同前面的函数参数assign的解释
s.append(str,2,string::npos)//不解释了
s.append(“my name is jiayp”);
s.append(“nico”,5);
s.append(5,’x’);
s.push_back(‘a’);//这个函数只能增加单个字符 对STL熟悉的理解起来很简单
也许你需要在string中间的某个位置插入字符串,这时候你可以用insert()函数,这个函数需要你指定一个安插位置的索引,被插入的字符串将放在这个索引的后面。
s.insert(0,”my name”);
s.insert(1,str);
这种形式的insert()函数不支持传入单个字符,这时的单个字符必须写成字符串形式(让人恶心)。既然你觉得恶心,那就不得不继续读下面一段话:为了插入单个字符,insert()函数提供了两个对插入单个字符操作的重载函数:insert(size_type index,size_type num,chart c)和insert(iterator pos,size_type num,chart c)。其中size_type是无符号整数,iterator是char*,所以,你这么调用insert函数是不行的:insert(0,1,’j’);这时候第一个参数将转换成哪一个呢?所以你必须这么写:insert((string::size_type)0,1,’j’)!第二种形式指出了使用迭代器安插字符的形式,在后面会提及。顺便提一下,string有很多操作是使用STL的迭代器的,他也尽量做得和STL靠近。
删除函数erase()的形式也有好几种(真烦!),替换函数replace()也有好几个。举例吧:
string s=”il8n”;
s.replace(1,2,”nternationalizatio”);//从索引1开始的2个替换成后面的C_string
s.erase(13);//从索引13开始往后全删除
s.erase(7,5);//从索引7开始往后删5个
主题 我的《C++ Primer》学习小记
ljl
ljl 发表于 2003-12-23 15:58:42 [50分]
--------------------------------------------------------------------------------
注:只是个人的一点纪录,如果对你有一点点帮助,偶就很知足了。写的不对请指出,不要扔蔬菜和肉蛋之类的好东西奥:))
第一章 开始
1. #include指示符读入指定文件的内容,它有两种格式
#include <some_file.h> :表明该文件是一个工程或标准头文件,查找过程会检查预定义的目录
#include “my_file.h”:表明该文件是用户提供的头文件,查找该文件将从当前文件目录开始。
2. 为了防止头文件被多次包含在一个源文件中,可用条件指示符防止,如:
#ifndef BOOKSTORE_H
#define BOOKSTORE_H
/* Bookstore.h的内容 */
#endif
3. 编译c++程序时,编译器自动定义一个预处理名字__cplusplus(注意两个下划线),可以根据它来判断该程序是否c++程序;在编译标准c时,编译器将自动定义名字__STDC__,二者不会同时被定义。另外两个比较有用的预定义名字是:__LINE__和__FILE__。前者记录文件已经被编译的行数,后者包含正在被编译的文件的名字,如
if( element_count == 0 )
cerr << "Error: " << __FILE__
<< " : line " << __LINE__
<< " element_count must be non-zero./n";
__TIME__:当前被编译文件的编译时间
__DATE__:当前被编译文件的编译日期
注:项2、3请参见程序ifdef_page10.cpp
4. assert( )是C语言标准库中提供的一个通用预处理宏。C à #include <assert.h>,CPP à #include <cassert>
5. 关于注释:/* */不能嵌套,非要嵌套须在/和*之间加空格,单行或半行用//进行注释较好
6.
第二章 C++浏览
1. 在内置数据类型(如int、float、double等)与标准库类的类型之间是复合类型,特别是指针和数组类型。数组是一种顺序容器,包含单一类型的元素。
2. 静态分配:编译器在处理程序源代码时分配,静态对象是有名字的变量,直接对其进行操作,分配和释放由编译器自动处理 --- 效率高但缺少灵活性
动态分配:程序执行时调用运行时刻库函数来分配,是没有名字的变量,须通过指针间接地对她进行操作,由new和delete进行分配和释放。如
int *pint = new int( 1024 ); ------ delete pint;
int *pia = new int[ 4 ]; ------ delete [] pia;
3. 基类和派生类的设计原则:
1) 如果希望阻止派生类直接访问某个成员,就把该成员声明为基类的private成员;如果确信某个成员希望被派生类直接访问,则声明为protected.
2) 找出类型相关的成员函数,并把这些成员函数标记为virtual(虚拟的) --- 基类里面只声明,具体的定义根据不同的派生类而不同。对于一个非虚拟函数的调用,编译器在编译时刻选择被调用的函数。而虚拟函数调用的决定则要等到运行时刻。在执行程序内部的每个调用点上,系统根据被调用对象的实际基类或派生类的类型来决定选择哪一个虚拟函数实例。
4. 不是所有的成员函数都能自动地随类模板的实例化而被实例化,只有真正被程序使用到的成员函数才会被实例化,这一般发生在程序生成过程中的一个独立阶段。
5. void min(double); 以传值的方式传递内建类型是一种常见而可接受的做法
void min(const elemType &); 传递对象应避免,应选择引用或者指针,假如void min(elemType),编译器会调用一个copy constructor以求为该对象制造一份副本,然后才将该副本以传值方式传送。
6. 基于异常的设计:
主要构成:
1) 程序中异常出现的点:一旦识别出异常,就导致抛出raise/throw异常,正常的程序就被挂起,直到异常被处理完毕。如:
if( !infile )
{
string errMsg(“unable to open file: “);
errMsg += filename;
throw errMsg;
}
2) 程序中异常被处理的点.典型地,程序异常的抛出与处理位于独立的函数或成员函数调用中.
7. assert只在程序的debug版时才有效,当表达式为假时,就会弹出一个assertion failed的对话框,提示出错。在release 中assert被完全忽略了,就象不存在一样,assert只是一种调试手段。
8. 名字空间机制允许我们封装名字,否则就有可能污染(影响)全局名字空间.
名字空间别名允许用一个可替代的,短的或更一般的名字与一个现有的名字空间关联:
namespace LIB = IBM_Canada_Laboratory;
namespace DFA = Disney_Feature_Animation;
9. 为了防止c++库的组件污染用户程序的全局名字空间,所有标准C++库的组件都声明在一个被称为std的名字空间内。所以即使我们在程序文本中包含了c++库文件,头文件中声明的组件在我们的头文件中也不是自动可见的。
10. 标准数组---向量:vector,如vector<int> ivec( 10 );
1) vector类模板支持“向现有的数组元素赋值”的概念以及“插入附加元素”的概念------可以在运行时刻动态增长;
2) vector只提供了一个最小集:如等于、小于操作符、size()、empty()等操作。而一些通用的操作如sort()、min()、max()、find()等等则作为独立的泛型算法被提供。
3) 可以使用迭代器对(iterator pair)来标记向量的起始和结束处。迭代器是一个支持指针类型抽象的类对象。begin()和end()
4) 可以应用到向量上的操作惊人地多,但是他们并不是作为vector类模板的成员函数来提供的,他们是以一个独立的泛型算法集的形式,有标准库提供。如:(#include <algorithm>)
a) 搜索算法: find()、find_if()、search()、binary_search()、count()、count_if().
b) 分类排序与通用排序算法:sort()、partial_sort()、merge()、partition()、rotate()、reverse()、random_shuffle().
c) 删除算法:unique()和remove()
d) 算术算法:accumulate()、partial_sum()、inner_product()和adjacent_difference().
e) 生成和变异算法:generate()、fill()、transformation()、copy()和for_each()
f) 关系算法: equal()、min() 和max()
5) vector<int> ivec(10,-1); 表示定义了包含10个int元素,每个元素都被初始化为-1;vector<int> ivec(&ia[2], &ia[5]); 表示用ia[2]、ia[3]、ia[4]来拷贝
6) STL中用法完全不同:首先定义一个空vector: vector<int> text; 赋值的操作不再是索引元素,而是用push_back()在vector后面插入一个元素。访问则用
cout << “words read are: /n”;
for( vector<string>::iterator it = text.begin();
it !=text.end(); ++it )
cout << *it << ‘ ‘;
cout << endl;
注意:不能混用这两种用法:如 vector<int> ivec; ivec[0] = 1024;是错误的,因为ivec还没有第一个元素,我们只能索引vector中已经存在的元素。
7) 对于const 向量的迭代子应该这样定义:vector<int> ::const_iterator it = ivec.begin();
8)
11.
第三章 C++数据类型
1. 文字常量:是不可寻址的,尽管也存在机器内存一个地方。20(十进制)--- 024(八进制)---0x14(十六进制)。
a) 字符型char:可以用一个机器字节来表示
b) 整型int、短整型short、长整型long:典型情况下,short半个字,int一个机器字,long为一个或两个机器字(32位机器中,int和long通常长度相同)
c) float、double和long double,分别为一个字、两个字,三个或四个字。
d) 避免将long写成 2l,最好用大写2L
e) aaa
f) 一些常用字符
l newline(换行符) /n
l horizontal tab(水平制表符) /t
l vertical tab(垂直制表符)
l backspace(退格键) /b
l carriage return(回车键) /r
l formfeed(进纸键) /f
l alert(beel)(响铃键) /a
l backslash(反斜杠键) //
l question mark(问号) /?
l single quote(单引号) /’
l double quote(双引号) /”
g)
2. 变量:是可寻址的,对于每一个变量,都有两个值与其相关联:1)数据值:存储在某个地址中,也被称为右值(rvalue--- are-value),文字和常量都可被用作右值;2)地址值:即存储数据值的那块内存的地址,也叫变量的左值(lvalue---ell-vlaue).
全局变量被初始化为0,局部变量或new则不提供,值是未定义的。
3. 不同数据类型的指针之间的区别不是在指针的表示上,也不再指针所特有的值(地址)上,而在于指针所指向的对象的类型上,指针的类型可以知是编译器怎样解释特定地址上内存的内容,以及该内存区域应该跨越多少内存单元。
4. 当指针持有0值时,表明它没有指向任何对象,或持有一个桶类型的数据对象的地址。
5. C++提供了一种特殊的指针类型来支持“仅仅是持有地址值(可能是把一个地址同另一个地址作比较)”:空(void*)类型指针,它可以被任何数据指针类型的地址值赋值(函数指针不能赋值给它)。如: int *pi = 0; double dval; double *pd = &dval;
void *pv = pi; pv = pd;
6. 字符串类型:2种
1) C风格的字符串:通过char*类型的指针来操纵它。标准c库提供了一组函数:
#include <cstring>
int strlen( const char* );
int strcmp( const char*, const char* );
char* strcpy( char*, const char* );
2) 字符串类型string,它包含一系列函数,如:
size(), empty(), c_str(),
3)
7. const限定修饰符
8. 引用
9. 枚举类型:不能使用枚举成员进行迭代
10. 复数类型 #include <complex>,复数对象有float、double或long double几种表示。
11. typedef名字提供了一种助记符
若:typedef char* cstring;
那么extern const csting cstr;中cstr的类型是什么?
指向const字符的指针???错!!!!!!
正确:Const修饰cstr的类型,cstr是一个指针,cstr是一个指向字符的const指针!!!
12. volatile限定修饰符:提示编译器,该对象的值可能在编译器未监测到的情况下被改变。
13. pair类型:使得我们可以在单个对象内部八项同类型或不同类型的两个值关联起来。
1) 头文件:#include <utility>
pair< string, string > author(“James”, “Joycer”);
2) 可以用成员访问符号访问pair中的单个元素,他们的名字为first和second,如:
string firstBook;
if(author.first ==”James” && author.second == “Joycer”)
firstBook = “Stephen Hero”;
3)
第四章 表达式
1. static_cast< int >(byte_value) 称为显式类型转换(explicit type conversion) 或强制类型转换(cast).
2. 标准C++头文件limits提供了与内置类型有关的信息,例如一个类型能表示的最大值和最小值。另外,C++编译系统也提供了标准C头文件climits和cfloat,他们定义了提供类似信息的预处理宏。
3. 对于逻辑与操作符(exp1 && exp2),一个很有价值的用法是:在某些使expr2的计算变得危险的边界条件出现前,先使expr1计算为false,如
while( ptr != 0 &&
prt ->value < upperBound &&
ptr ->value >= 0 &&
notFound( ia[ ptr->value ] ))
{ … }
值为0的指针不指向任何对象,所以当判断ptr !=0 为false后,后面的不予执行,也就避免了操作空指针的麻烦。同样,只有当前三个操作数计算的结果全为true,最后一个操作数才会安全的被计算。
对于逻辑或,应该也存在同样的问题,只要第一个判断式为true,整个条件就为true,不过这也为测试带来了“隐患”,必须用false || true进行一遍。
4. 一个对象只能被初始化一次(定义的时候),但是可以被赋值多次。赋值操作符的左操作数必须是左值 --- 有一个相关联的、可写的地址值。但是,只有左值还不够,如: const int i = 1; i = 2;是错误的,因为有const修饰符。又如,对于数组ia:ia=pia;是错误的,尽管ia也是左值。
5. 只有每个被赋值的操作数都是相同的数据类型,赋值操作符才可以被连接在一起使用:int a, b; a = b = 2; 正确!
6. 可以用成员访问语法来读取实部或虚部:complex_obj.real()和complex_obj.imag(),或者real(complex_obj)、imag( complex_obj )
7. sizeof操作符:返回一个对象或类型名的字节长度,有三种形式,返回值的类型是size_t。
#include <cstddef>
int ia[] = { 0, 1, 2};
size_t array_size = sizeof ia; //返回整个数组的大小(整个数组的字节长度,而不是第一个元素的长度,也不是ia包含的元素的个数)
size_t element_size = array_size / sizeof( int );//返回int类型的大小
又如,int *pi = new int[ 3 ];
size_t pointer_size = sizeof( pi );
返回的值是指向int型的指针的字节长度,而不是pi指向的数组的长度
l sizeof操作符应用在char类型上时,在所有的C++实现中都是1。
l sizeof操作夫在编译时刻计算,因此可被看作常量表达式,可用作数组的维数或模板的非类型参数。
8. new和delete表达式
1) 系统为每个程序都提供了一个在程序执行时可用的内存池,被称为程序的空闲存储区(free store)或堆(heap),运行时刻的内存分配被称为动态内存分配。
2) 所有从空闲存储区分配的对象都是未命名的。New表达式并不返回实际被分配的对象,而是返回这个对象的地址。对象的所有操作都通过这个地址间接来完成。
9. 逗号操作符的表达式从左向右计算,结果是最右边的表达式的值。
10. 位操作符
1) 强烈建议使用无符号类型整数作为位向量。
2) bitset类 #include <bitset>, 相关的成员函数见139页
11.
第五章 语句
1. 几个编码规则:
1) 避免使用全局变量,并适当地使用 namespace
2) 尽可能把所有的声明置于块一开始的地方,以便能轻易看见所有被定义以及被使用的对象
3) 当声明涉及许多修饰词时,每一个应占用独立的一行,以免产生“究竟是那一个对象被修饰”的困惑。
4) 为所有的变量设立初值。
2. isalpha()是标准C库的一个例程:如果它的参数是一个英文字母,则返回true,头文件为 ctype.h。
3. 把一条声明语句放在与case或default相关联的语句中是非法的,除非放在一个语句块中,如:
case illegal_definition:
//错误:声明语句必须放在语句块中
string file_name = get_file_name();
break;
上面须在case后加{ … }
4. do
statement
while(condiction) 结构至少可以保证循环一次,而while(condiction)则有可能一次都不执行。
5. abort()终止整个程序,包含在c头文件cstdlib中;一般情况下,应该避免终止程序。一般来说,抛出异常比终止程序更好一些。
6. 关于成员函数重载的问题:当你写了一个带有默认参数的函数时,你可以在调用时不特别声明那些默认的参数。反过来就是说,当你调用了一个find(int)时,它可以匹配那个不带默认参数的函数定义,也可以匹配那个带有默认参数的定义。这样它就不知道到底需要调用那个函数了。如:
#include <iostream>
using namespace std;
class test
{
public:
test(){}
~test(){}
void fun( int );
void fun( int, int );
};
void test::fun(int i){ cout << "One Parameter!" << endl; }
//void test::fun(int i, int j = 0 )//错误
void test::fun(int i, int j ) //正确
{ cout << "Two Parameter!" << endl; }
int main(){ test myTest; myTest.fun( 1 ); return 0; }
7.
第六章 抽象数据类型
1. 顺序容器拥有由单一类型元素组成的一个有序集合。
a) 两个主要的顺序容器是list和vector,还有双端队列deque;关联容器支持查询一个元素是否存在,并且可以有效地获取元素,如map(映射)和set(集合).map是一个键/值(key/value)对:前者用于查询,值包含我们希望使用的数据。如map可以很好地支持电话目录,键是人名,值是相关联的电话号码。Set包含一个单一键值,有效支持关于元素是否存在的查询。map和set都只包含每个键的唯一出现,multimap和multiset支持同一个键的多次出现。
b) 容量是指在容器下一次需要增长之前能够被加入到容器中的元素的总数(容量只与连续存储的容器相关,如vector、deque或string.list不要求容量)。可用capacity()获得一个容器的容量,而长度(size)是指容器当前拥有元素的个数,可用size()操作。
c) 表6.1 长度、容量、以及各种数据类型
数据类型 长度(字节) 初始插入后的容量
Int 4 256
Double 8 128
简单类(simple class) 12 85
string 12 85
大型简单类 8000 1
大型复杂类(large complex class) 8000 1
关于插入时间的比较,参见p216页
结论:对于小的数据类型,vector的性能要比list好得多,而对于大型的数据类型则相反,list的性能要好得多。区别是由于vector需要重新增长以及拷贝元素。
但是,数据类型的长度并不是影响容器性能的唯一标准,数据类型的复杂性也会影响到元素插入的性能。因为简单类对象和大型简单类对象通过按位拷贝插入(一个对象的所有位被拷贝到第二个对象的位中),而string类对象和大型复杂类对象通过调用拷贝构造函数来插入。
d) 一些操作函数:
l reserve()操作允许程序员将容器的容量设置成一个显式指定的值。如:vec.reserve(32);
l empty()判断是否为空
l push_back()插入容器尾部
l 对于list和queue,也支持push_front(),插入前端。
l resize():重新设置容器的长度
l 容器可以比较大小:相等(所有元素相等而且含有相同数目的元素);大于小于(比较第一个不相等的元素来决定)
l insert(p1,p2): p1参数指向容器中某个位置的iterator,第二个参数p2是要插入的值。
l insert(p1,p2,p3): p1同上;p2是插入数量;p3是插入值。
l insert(p1,p2,p3): p1同上;p2-p3是指插入范围值。P225。
l find(p1,p2,p3): P1-p2指起始范围,p3查找值。
l erase(): 删除一个或一段元素
l pop_back()删除容器末元素---它不返回元素,只是简单的删除它。
l swap()对换元素。
l istream& getline( istream &is, string str, char delimiter ); 读取istream对象,向string对象插入字符,包括空格,直到遇到分割符、文件结束,或者被读入的字符序列等于string对象的max_size()值,在该点处读入操作失败。
e) 能够定义的容器类型有三个限制(只适用于用户定义的类类型,所有预定义类型包括指针都满足这些限制)
l 元素类型必须支持等于操作副
l 元素类型必须支持小于操作副
l 元素类型必须支持一个缺省值(对于类,即指缺省构造函数)。
f)
2. vector、deque和list都是动态增长的,选择标准主要是关注插入特性以及对元素的后续访问要求。
1) vector:连续的内存区域,随机访问效率高,插入删除效率低。Vector并不是随每一个元素的插入而增长自己,而是当vector需要增长自身时,它实际分配的空间比当前所需的空间要多一些,也就是说,它分配了一些额外的内存容量,或者说他预留了这些存储区(分配的额外容量的确切数目由具体实现定义)。实际上,对于小的对象,vector在实践中比list效率更高。
Vector的动态自我增长越频繁,元素插入的开销就越大。两种解决方案:
a) 当vector开销变大时,把vector转换成list.
b) 更常用的方案时,通过指针间接存储复杂的类对象。首先,容量从1增加到256,重新分配的次数大大减少。其次,指向类对象的指针的拷贝和释放不需要调用该类的拷贝构造函数和析构函数。
2) list:非连续的内存区域,并通过一对指向首尾元素的指针双向联结起来,从而允许向前向后两个方向进行遍历。插入和删除效率高,随机访问支持不好。
3) deque也表示一段连续的内存区域,支持高效在首部插入和删除元素,通过两级数据结构来实现,一级表示实际的容器,第二级指向容器的首和尾。如果容器的主要行为是在前段插入元素,则deque比vector效率高。
选择顺序容器类型的一些准则:
a) 随机访问一个容器,vector比list好的多。
b) 已知要存储元素的个数,vector又是一个比list好的选择。
c) 不只是在容器两端插入和删除元素,则list比vector好。
d) Unless we need to insert or delete elements at the front of the container, a vector is preferable to a deque.(除非我们需要在容器首部插入和删除元素,否则vector要比deque好)
如果两者都需要,则要根据程序的主要操作来决定,如果两种容器都不够满意,只能自己设计更复杂的数据结构。
3. iterator: end()指向最后一个元素的下一个位置;对于const容器则用const_iterator
4. string类:
find()返回匹配子串的第一个字符的索引位置,或返回一个特定的值:string::npos表明没有匹配。
find_first_of()查找预备搜索字符串中任意一个字符相匹配的第一次出现,并返回索引位置。可以有1或2个参数,详见P231
substr()生成现有string对象的子串的一个拷贝
rfind()查找最后的指定子串的出现
find_first_not_of()查找第一个不与要搜索字符串的任意字符相匹配的字符。P235
find_last_of()查找字符串中的“与搜索任意字符相匹配“的最后一个字符。
Find_last_not_of()查找字符串中的“与搜索字符串任意字符全不匹配“的最后一个字符。
tolower()大写转换小写
5. 当我们只想知道一个值是否存在时,set最有用,希望存储(修改)一个相关的值时,map最有用。Count(KeyValue)返回map中keyValue出现的次数;Find(keyValue)返回指向该实例的iterator,如果不存在,则返回等于end()的iterator.size()返回map个数,empty()判断空。
6. #pragma warning(disable : 4786)可以消除vc中的警告(warnings).
7. set只是键的集合。
8.
第七章 函数
1. 函数调用导致两件事情发生,如果函数已经被声明为inline,则函数体可能已经在编译期间他的调用点上就被展开;如果没有被声明为inline,则函数在运行才被调用。
2. 引用的应用:a) 用指针类型来改变实参的值 b) 向主调函数返回额外的结果 c) 向函数传递大型类对象,如果为了防止更改类对象的值,可以用const &。
3. 引用还是指针?引用必须被初始化为指向一个对象,不能再指向其他对象。而指针可以指向一系列不同的对象也可以什么都不指向。
4. 在c++中,数组永远不会按值传递,它是传递第一个元素的指针,所以在被调函数内对参数数组地改变将被用到数组实参上而不是本地拷贝上。如,
void fun(int *a)
{
a[0] = 1; a[1] = 2;
}
int main()
{
int aa[ 2 ] = {3,3}; cout << aa[ 0 ] << " " << aa[1] << endl; //output: 3 3
fun(aa); cout << aa[ 0 ] << " " << aa[1] << endl; // output: 1, 2
return 0;
}
当用作实参的数组必须保持不变时,函数可以通过把参数类型声明为const来表明不希望改变数组元素。另外,数组的长度不会被检测出,必须额外提供;另一种机制是将参数声明为数组的引用。对于多维数组,必须指明第一维以外的所有维的长度。
5. 多个实参时,可以省略右边的,但不能省略左边的实参。
6. 缺省情况下,函数的返回值是按值传递的,这意味着得到控制权的函数将接受返回语句中制定的表达式的拷贝。当返回大型类对象时,最好用引用或指针。
7. inline函数:在程序中每个调用点上被“内联地”展开。它必须在调用函数的每个文本文件中定义。建议:把inline函数的定义放到头文件中,在每个调用该inline函数的文件中包含该头文件,这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。
8. 链接指示符:extern “C”:单一语句形式(single statement)和复合语句形式(compound statement). 它不能出现在函数体中(编译出错)
extern “C”void exit(int);//single
extern “C”{ //compound
int printf( const char* … );
int scanf( const char* … );
}
extern “C”{ #include <cmath> }
注:只是个人的一点纪录,如果对你有一点点帮助,偶就很知足了。写的不对请指出,不要扔蔬菜和肉蛋之类的好东西奥:))
第一章 开始
1. #include指示符读入指定文件的内容,它有两种格式
#include <some_file.h> :表明该文件是一个工程或标准头文件,查找过程会检查预定义的目录
#include “my_file.h”:表明该文件是用户提供的头文件,查找该文件将从当前文件目录开始。
2. 为了防止头文件被多次包含在一个源文件中,可用条件指示符防止,如:
#ifndef BOOKSTORE_H
#define BOOKSTORE_H
/* Bookstore.h的内容 */
#endif
3. 编译c++程序时,编译器自动定义一个预处理名字__cplusplus(注意两个下划线),可以根据它来判断该程序是否c++程序;在编译标准c时,编译器将自动定义名字__STDC__,二者不会同时被定义。另外两个比较有用的预定义名字是:__LINE__和__FILE__。前者记录文件已经被编译的行数,后者包含正在被编译的文件的名字,如
if( element_count == 0 )
cerr << "Error: " << __FILE__
<< " : line " << __LINE__
<< " element_count must be non-zero./n";
__TIME__:当前被编译文件的编译时间
__DATE__:当前被编译文件的编译日期
注:项2、3请参见程序ifdef_page10.cpp
4. assert( )是C语言标准库中提供的一个通用预处理宏。C à #include <assert.h>,CPP à #include <cassert>
5. 关于注释:/* */不能嵌套,非要嵌套须在/和*之间加空格,单行或半行用//进行注释较好
6.
第二章 C++浏览
1. 在内置数据类型(如int、float、double等)与标准库类的类型之间是复合类型,特别是指针和数组类型。数组是一种顺序容器,包含单一类型的元素。
2. 静态分配:编译器在处理程序源代码时分配,静态对象是有名字的变量,直接对其进行操作,分配和释放由编译器自动处理 --- 效率高但缺少灵活性
动态分配:程序执行时调用运行时刻库函数来分配,是没有名字的变量,须通过指针间接地对她进行操作,由new和delete进行分配和释放。如
int *pint = new int( 1024 ); ------ delete pint;
int *pia = new int[ 4 ]; ------ delete [] pia;
3. 基类和派生类的设计原则:
1) 如果希望阻止派生类直接访问某个成员,就把该成员声明为基类的private成员;如果确信某个成员希望被派生类直接访问,则声明为protected.
2) 找出类型相关的成员函数,并把这些成员函数标记为virtual(虚拟的) --- 基类里面只声明,具体的定义根据不同的派生类而不同。对于一个非虚拟函数的调用,编译器在编译时刻选择被调用的函数。而虚拟函数调用的决定则要等到运行时刻。在执行程序内部的每个调用点上,系统根据被调用对象的实际基类或派生类的类型来决定选择哪一个虚拟函数实例。
4. 不是所有的成员函数都能自动地随类模板的实例化而被实例化,只有真正被程序使用到的成员函数才会被实例化,这一般发生在程序生成过程中的一个独立阶段。
5. void min(double); 以传值的方式传递内建类型是一种常见而可接受的做法
void min(const elemType &); 传递对象应避免,应选择引用或者指针,假如void min(elemType),编译器会调用一个copy constructor以求为该对象制造一份副本,然后才将该副本以传值方式传送。
6. 基于异常的设计:
主要构成:
1) 程序中异常出现的点:一旦识别出异常,就导致抛出raise/throw异常,正常的程序就被挂起,直到异常被处理完毕。如:
if( !infile )
{
string errMsg(“unable to open file: “);
errMsg += filename;
throw errMsg;
}
2) 程序中异常被处理的点.典型地,程序异常的抛出与处理位于独立的函数或成员函数调用中.
7. assert只在程序的debug版时才有效,当表达式为假时,就会弹出一个assertion failed的对话框,提示出错。在release 中assert被完全忽略了,就象不存在一样,assert只是一种调试手段。
8. 名字空间机制允许我们封装名字,否则就有可能污染(影响)全局名字空间.
名字空间别名允许用一个可替代的,短的或更一般的名字与一个现有的名字空间关联:
namespace LIB = IBM_Canada_Laboratory;
namespace DFA = Disney_Feature_Animation;
9. 为了防止c++库的组件污染用户程序的全局名字空间,所有标准C++库的组件都声明在一个被称为std的名字空间内。所以即使我们在程序文本中包含了c++库文件,头文件中声明的组件在我们的头文件中也不是自动可见的。
10. 标准数组---向量:vector,如vector<int> ivec( 10 );
1) vector类模板支持“向现有的数组元素赋值”的概念以及“插入附加元素”的概念------可以在运行时刻动态增长;
2) vector只提供了一个最小集:如等于、小于操作符、size()、empty()等操作。而一些通用的操作如sort()、min()、max()、find()等等则作为独立的泛型算法被提供。
3) 可以使用迭代器对(iterator pair)来标记向量的起始和结束处。迭代器是一个支持指针类型抽象的类对象。begin()和end()
4) 可以应用到向量上的操作惊人地多,但是他们并不是作为vector类模板的成员函数来提供的,他们是以一个独立的泛型算法集的形式,有标准库提供。如:(#include <algorithm>)
a) 搜索算法: find()、find_if()、search()、binary_search()、count()、count_if().
b) 分类排序与通用排序算法:sort()、partial_sort()、merge()、partition()、rotate()、reverse()、random_shuffle().
c) 删除算法:unique()和remove()
d) 算术算法:accumulate()、partial_sum()、inner_product()和adjacent_difference().
e) 生成和变异算法:generate()、fill()、transformation()、copy()和for_each()
f) 关系算法: equal()、min() 和max()
5) vector<int> ivec(10,-1); 表示定义了包含10个int元素,每个元素都被初始化为-1;vector<int> ivec(&ia[2], &ia[5]); 表示用ia[2]、ia[3]、ia[4]来拷贝
6) STL中用法完全不同:首先定义一个空vector: vector<int> text; 赋值的操作不再是索引元素,而是用push_back()在vector后面插入一个元素。访问则用
cout << “words read are: /n”;
for( vector<string>::iterator it = text.begin();
it !=text.end(); ++it )
cout << *it << ‘ ‘;
cout << endl;
注意:不能混用这两种用法:如 vector<int> ivec; ivec[0] = 1024;是错误的,因为ivec还没有第一个元素,我们只能索引vector中已经存在的元素。
7) 对于const 向量的迭代子应该这样定义:vector<int> ::const_iterator it = ivec.begin();
8)
11.
第三章 C++数据类型
1. 文字常量:是不可寻址的,尽管也存在机器内存一个地方。20(十进制)--- 024(八进制)---0x14(十六进制)。
a) 字符型char:可以用一个机器字节来表示
b) 整型int、短整型short、长整型long:典型情况下,short半个字,int一个机器字,long为一个或两个机器字(32位机器中,int和long通常长度相同)
c) float、double和long double,分别为一个字、两个字,三个或四个字。
d) 避免将long写成 2l,最好用大写2L
e) aaa
f) 一些常用字符
l newline(换行符) /n
l horizontal tab(水平制表符) /t
l vertical tab(垂直制表符)
l backspace(退格键) /b
l carriage return(回车键) /r
l formfeed(进纸键) /f
l alert(beel)(响铃键) /a
l backslash(反斜杠键) //
l question mark(问号) /?
l single quote(单引号) /’
l double quote(双引号) /”
g)
2. 变量:是可寻址的,对于每一个变量,都有两个值与其相关联:1)数据值:存储在某个地址中,也被称为右值(rvalue--- are-value),文字和常量都可被用作右值;2)地址值:即存储数据值的那块内存的地址,也叫变量的左值(lvalue---ell-vlaue).
全局变量被初始化为0,局部变量或new则不提供,值是未定义的。
3. 不同数据类型的指针之间的区别不是在指针的表示上,也不再指针所特有的值(地址)上,而在于指针所指向的对象的类型上,指针的类型可以知是编译器怎样解释特定地址上内存的内容,以及该内存区域应该跨越多少内存单元。
4. 当指针持有0值时,表明它没有指向任何对象,或持有一个桶类型的数据对象的地址。
5. C++提供了一种特殊的指针类型来支持“仅仅是持有地址值(可能是把一个地址同另一个地址作比较)”:空(void*)类型指针,它可以被任何数据指针类型的地址值赋值(函数指针不能赋值给它)。如: int *pi = 0; double dval; double *pd = &dval;
void *pv = pi; pv = pd;
6. 字符串类型:2种
1) C风格的字符串:通过char*类型的指针来操纵它。标准c库提供了一组函数:
#include <cstring>
int strlen( const char* );
int strcmp( const char*, const char* );
char* strcpy( char*, const char* );
2) 字符串类型string,它包含一系列函数,如:
size(), empty(), c_str(),
3)
7. const限定修饰符
8. 引用
9. 枚举类型:不能使用枚举成员进行迭代
10. 复数类型 #include <complex>,复数对象有float、double或long double几种表示。
11. typedef名字提供了一种助记符
若:typedef char* cstring;
那么extern const csting cstr;中cstr的类型是什么?
指向const字符的指针???错!!!!!!
正确:Const修饰cstr的类型,cstr是一个指针,cstr是一个指向字符的const指针!!!
12. volatile限定修饰符:提示编译器,该对象的值可能在编译器未监测到的情况下被改变。
13. pair类型:使得我们可以在单个对象内部八项同类型或不同类型的两个值关联起来。
1) 头文件:#include <utility>
pair< string, string > author(“James”, “Joycer”);
2) 可以用成员访问符号访问pair中的单个元素,他们的名字为first和second,如:
string firstBook;
if(author.first ==”James” && author.second == “Joycer”)
firstBook = “Stephen Hero”;
3)
浙公网安备 33010602011771号