??xml version="1.0" encoding="utf-8" standalone="yes"?>
{
FILE *fp=fopen("1.txt","r+");
fputc('x',fp);
fputc('x',fp);
fputc('x',fp);
return 0;
}
{
FILE *fp=fopen("1.txt","rb+");
char c=fgetc(fp);
fputc('x',fp);
fputc('x',fp);
fputc('x',fp);
return 0;
}
《Unix环境高~程》在使用d方式打开文gӞtype中的+PQ输出的后面不能直接跟输入,输入的后面也不能直接跟输出,否则可能会出错。如果需要输入相q,则中?
需调用fflush,fseek,fsetpos或rewind{操作?br> 既然q样Q那我们试一下,看看能不能解决问题,在上面代码中fgetc和fputc中间加入
fgetpos(fp,&pos);
fsetpos(fp,&pos);
]]>
alias g++='g++ -Wall'
q样以后每次使用gcc/g++的时候它p动启?Wall选项?br>
]]>
http://lmlf001.blog.sohu.com/
]]>
多重l承
前面我们介绍的派生类只有一个基c,UCؓ单基z或单一l承。在实际q用中,我们l常需要派生类同时h多个基类Q这U方法称为多基派生或多重l承?/span>
2.1
多重l承的声明:
?/span>
C++
中,声明h两个以上基类的派生类与声明单基派生类的Ş式类|只需要l承的多个基cȝ逗号分开卛_?/span>
在多重承中Q公有派生和U有z对于基类成员在派生类的可讉K性与单承的规则相同?/span>
另外Q对基类成员的访问必L无二义的Q若两个基类中具有同名的数据成员或成员函敎ͼ使用成员名限定来消除二义性,若派生类中新增成员或成员函数与基cL员或成员函数同名Q则zcM覆盖外层同名成员Q也M用作用域分LW?/span>
2.2
多重l承的构造函数和析构函数Q?/span>
多重l承的构造函数的定义形式与单l承构造函数的定义形式cMQ只?/span>
n
个基cȝ构造函C间用“,”分隔?/span>
多重l承的构造函数的执行序与单l承构造函数的执行序相同Q也是遵循先执行基类的构造函敎ͼ再执行对象成员的构造函敎ͼ最后执行派生类构造函数的原则。在多个基类之间Q则严格按照zcd明是从左到右的顺序来排列先后。而析构函数的执行序与构造函数的执行序相反?/span>
2.3
虚基c?/span>
:
如果某个zcȝ部分或全部直接基cL从另一个共同的基类z而来Q在q些基类中,从上一U基cȝ承来的成员就有相同的名称Q则在这个派生类中访问这个共同的基类中的成员Ӟ可能会生二义性,此时Q可定义虚基cR这p求在其直接基cȝ定义中,使用关键?/span>
virtual
那个共同的基类定义基类Q其语法形式如下Q?/span>
class
zcdQ?/span>
virtual
z方式
基类
虚基cȝ初始化与一般的多重l承的初始化在语法上是一L
Q但构造函数的调用序不同Q虚基类构造函数的调用序是这栯定的Q?/span>
1)
在同一层次中,先调用虚基类的构造函敎ͼ接下来依ơ是非虚基类的构造函敎ͼ对象成员的构造函敎ͼzcȝ构造函数?/span>
2)
若同一层次中包含多个虚基类Q这些虚基类的构造函数按对他们说明的先后ơ序调用
3)
若虚基类由非虚基cL生而来Q则仍然先调用基cL造函敎ͼ再调用派生类构造函数?br />
]]>
volatile是c/c++中一个鲜Zh知的关键?该关键字告诉~译器不要持有变量的临时拯,它可以适用于基cd
如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,l构或?br />cȝ所有成员都会被视ؓvolatile.
使用volatileq不会否定对CRITICAL_SECTION,Mutex,Event{同步对象的需?br />例如Q?br />int i;
i = i + 3;
无论如何QL会有一段旉Qi会被攑֜一个寄存器中,因ؓ术q算只能在寄存器中进行。一般来_volatitle
关键字适用于行与行之间Q而不是放在行内?/font>
我们先来实现一个简单的函数Q来观察一下由~译器生出来的汇编代码中的不之处Qƈ观察volatile关键字如何修?br />q个不之处。在q个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一U高度浪费CPU旉的@环方?
void getKey(char* pch)
{
while (*pch == 0)
;
}
当你在VC开发环境中最优化选项都关闭之后,~译q个E序Q将获得以下l果(汇编代码)
; while (*pch == 0)
$L27
; Load the address stored in pch
mov eax, DWORD PTR _pch$[ebp]
; Load the character into the EAX register
movsx eax, BYTE PTR [eax]
; Compare the value to zero
test eax, eax
; If not zero, exit loop
jne $L28
;
jmp $L27
$L28
;}
q段没有优化的代码不断的载入适当的地址Q蝲入地址中的内容Q测试结果。效率相当的低,但是l果非常准确
现在我们再来看看编译器的所有最优化选项开关都打开以后Q重新编译程序,生成的汇~代码,和上面的代码
比较一下有什么不?br />;{
; Load the address stored in pch
mov eax, DWORD PTR _pch$[esp-4]
; Load the character into the AL register
movsx al, BYTE PTR [eax]
; while (*pch == 0)
; Compare the value in the AL register to zero
test al, al
; If still zero, try again
je SHORT $L84
;
;}
从代码的长度可以看出来Q比没有优化的情况要短的多。需要注意的是编译器把MOV指o攑ֈ了@环之外。这?br />单线E中是一个非常好的优化,但是Q在多线E应用程序中Q如果另一个线E改变了变量的|则@环永q不?br />l束。被试的值永q被攑֜寄存器中Q所以该D代码在多线E的情况下,存在一个巨大的BUG。解x法是重新
写一ơgetKey函数Qƈ把参数pch声明为volatile,代码如下Q?/font>
void getKey(volatile char* pch)
{
while (*pch == 0)
;
}
q次的修改对于非最优化的版本没有Q何媄响,下面L最优化后的l果Q?/font>
;{
; Load the address stored in pch
mov eax, DWORD PTR _pch$[esp-4]
; while (*pch == 0)
$L84:
; Directly compare the value to zero
cmp BYTE PTR [eax], 0
; If still zero, try again
je SHORT $L84
;
;}
q次的修改结果比较完,地址不会改变Q所以地址声明被移动到循环之外。地址内容是volatile,所以每ơ@?br />之中它不断的被重新检查?/font>
把一个const volatile变量作ؓ参数传递给函数是合法的。如此的声明意味着函数不能改变变量的|但是变量?br />值却可以被另一个线E在M旉改变掉?/font>
另:
如果在一个多U程E序中想在两个函C׃n一个局部变量,需把变量声明ؓvolatilecd
例如Q?p>上例中,函数f1()中的循环控制条g永远为真Q字W串"helo"得不到打印?/p>
如果把f1()的参数类型改为volatitle void *x,则打印命令被实现?br />
如果一个类的成员函数被声明为constcd,表示该函C会改变对象的状?也就?br />该函C会修改类的非静态数据成?但是有些时候需要在该类函数中对cȝ数据成员
q行赋?q个时候就需要用?span class="hilite1">mutable关键字了
mutable关键字提C编译器该变量可以被cȝconst函数修改
In C++ it is possible to declare constructors for a class, taking a
single parameter, and use those constructors for doing type conversion.
For example:
class A {
public:
A(int);
};
void f(A) {}
void g()
{
A a1 = 37;
A a2 = A(47);
A a3(57);
a1 = 67;
f(77);
}
A declaration like:
A a1 = 37;
says to call the A(int) constructor to create an A object from the integer value. Such a constructor is called a "converting constructor".
However, this type of implicit conversion can
be confusing, and there is a way of disabling it, using a new keyword
"explicit" in the constructor declaration:
Using the explicit keyword, a constructor is declared to beclass A {
public:
explicit A(int);
};
void f(A) {}
void g()
{
A a1 = 37; // illegal
A a2 = A(47); // OK
A a3(57); // OK
a1 = 67; // illegal
f(77); // illegal
}
class A {
public:
explicit A(int);
};
void f(A) {}
void g()
{
A a1 = A(37);
A a2 = A(47);
A a3(57);
a1 = A(67);
f(A(77));
}
Note that an expression such as:
A(47)
is closely related to function-style casts supported by C++. For example:
double d = 12.34;
int i = int(d);
q是More Effecitve C++里的W二条对cd转换讲的很好Q也很基好懂?br />Item
M2Q尽量用C++风格的类型{?br />仔细xC卑贱的类型{换功能(castQ,其在E序设计中的Cpgoto语句一样o人鄙视。但是它q不是无法o人忍受,因ؓ当在某些紧要的关_cd转换q是必需的,q时它是一个必需品?br />不过C风格的类型{换ƈ不代表所有的cd转换功能?br />一
来它们过于粗鲁,能允怽在Q何类型之间进行{换。不q如果要q行更精的cd转换Q这会是一个优炏V在q些cd转换中存在着巨大的不同,例如把一个指?
const对象的指针(pointer-to-const-objectQ{换成指向非const对象的指针(pointer-to-non-const
-objectQ?即一个仅仅去除const的类型{?Q把一个指向基cȝ指针转换成指向子cȝ指针Q即完全改变对象cdQ。传l的C风格的类型{换不
对上qCU{换进行区分。(q一点也不o人惊Ӟ因ؓC风格的类型{换是为C语言设计的,而不是ؓC++语言设计的)?br />二来C风格的类型{换在E?
序语句中难以识别。在语法上,cd转换由圆括号和标识符l成Q而这些可以用在CQ+中的M地方。这使得回答象这样一个最基本的有关类型{换的问题变得?
困难Q“在q个E序中是否用了cd转换Q”。这是因Zh工阅d可能忽略了类型{换的语句Q而利用象grep的工L序也不能从语句构成上区分出它?
来?br />C++通过引进四个新的cd转换操作W克服了C风格cd转换的缺点,q四个操作符?
static_cast, const_cast, dynamic_cast,
和reinterpret_cast。在大多数情况下Q对于这些操作符你只需要知道原来你习惯于这样写Q?br />(type)
expression
而现在你d该这样写Q?br />static_cast<type>(expression)
例如Q假设你x一个int转换成doubleQ以便让包含intcd变量的表辑ּ产生出QҎ值的l果。如果用C风格的类型{换,你能q样写:
int
firstNumber, secondNumber;
...
double result =
((double)firstNumber)/secondNumberQ?br />如果用上q新的类型{换方法,你应该这样写Q?br />double
result =
static_cast<double>(firstNumber)/secondNumber;
q样的类型{换不论是对h工还是对E序都很Ҏ识别?br />static_cast
在功能上基本上与C风格的类型{换一样强大,含义也一栗它也有功能上限制。例如,你不能用static_cast象用C风格的类型{换一h
struct转换成intcd或者把doublecd转换成指针类型,另外Qstatic_cast不能从表辑ּ中去除const属性,因ؓ另一个新的类
型{换操作符const_cast有这L功能?br />其它新的C++cd转换操作W被用在需要更多限制的地方。const_cast用于cd转换掉表
辑ּ的const或volatileness属性。通过使用const_castQ你向h们和~译器强调你通过cd转换惛_的只是改变一些东西的
constness或者volatileness属性。这个含义被~译器所U束。如果你试图使用const_cast来完成修改constness
或者volatileness属性之外的事情Q你的类型{换将被拒l。下面是一些例子:
class Widget { ...
};
class SpecialWidget: public Widget { ... };
void
update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const
对象?br />const SpecialWidget& csw = sw; // csw 是sw的一个引?br />// 它是一个const
对象
update(&csw); // 错误!不能传递一个const SpecialWidget* 变量
//
l一个处理SpecialWidget*cd变量的函?br />update(const_cast<SpecialWidget*>(&csw));
//
正确Qcsw的const被显C地转换掉(
//
csw和sw两个变量值在update
//函数中能被更斎ͼ
update((SpecialWidget*)&csw);
//
同上Q但用了一个更难识?br />//的C风格的类型{?br />Widget *pw = new
SpecialWidget;
update(pw); // 错误Qpw的类型是Widget*Q但?br />//
update函数处理的是SpecialWidget*cd
update(const_cast<SpecialWidget*>(pw));
//
错误Qconst_cast仅能被用在媄?br />// constness or volatileness的地方上?
//
不能用在向承子c进行类型{换?br />到目前ؓ止,const_cast最普通的用途就是{换掉对象的const属性?br />W?
二种Ҏ的类型{换符是dynamic_castQ它被用于安全地沿着cȝl承关系向下q行cd转换。这是_你能用dynamic_cast把指向基
cȝ指针或引用{换成指向其派生类或其兄弟cȝ指针或引用,而且你能知道转换是否成功。失败的转换返回空指针Q当Ҏ针进行类型{换时Q或者抛出异?
Q当对引用进行类型{换时Q:
Widget
*pw;
...
update(dynamic_cast<SpecialWidget*>(pw));
//
正确Q传递给update函数一个指?br />// 是指向变量类型ؓSpecialWidget的pw的指?br />//
如果pw实指向一个对?
// 否则传递过ȝɽI指针?br />void updateViaRef(SpecialWidget&
rsw);
updateViaRef(dynamic_cast<SpecialWidget&>(*pw));
//正确。传递给updateViaRef函数
//
SpecialWidget pw 指针Q如果pw
// 实指向了某个对?br />//
否则抛出异?br />dynamic_casts在帮助你览l承层次上是有限制的。它不能被用于缺乏虚函数的类型上Q参见条ƾM24Q,也不能用它来转换掉constnessQ?br />int
firstNumber, secondNumber;
...
double result =
dynamic_cast<double>(firstNumber)/secondNumber;
//
错误Q没有承关p?br />const SpecialWidget
sw;
...
update(dynamic_cast<SpecialWidget*>(&sw));
//
错误! dynamic_cast不能转换
//
掉const?br />如你惛_没有l承关系的类型中q行转换Q你可能惛_static_cast。如果是Z去除constQ你d用const_cast?br />q四个类型{换符中的最后一个是reinterpret_cast。用这个操作符的类型{换,其的转换l果几乎都是执行期定义(implementation-definedQ。因此,使用reinterpret_casts的代码很隄植?br />reinterpret_casts的最普通的用途就是在函数指针cd之间q行转换。例如,假设你有一个函数指针数l:
typedef
void (*FuncPtr)(); // FuncPtr is 一个指向函?br />// 的指针,该函数没有参?br />//
q回值类型ؓvoid
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
//
10个FuncPtrs指针的数l?br />让我们假设你希望Q因为某些莫名其妙的原因Q把一个指向下面函数的指针存入funcPtrArray数组Q?br />int
doSomething();
你不能不l过cd转换而直接去做,因ؓdoSomething函数对于funcPtrArray数组来说有一个错误的cd。在FuncPtrArray数组里的函数q回值是voidcdQ而doSomething函数q回值是intcd?br />funcPtrArray[0]
= &doSomething; //
错误Q类型不匚w
reinterpret_cast可以让你qɾ~译器以你的Ҏȝ待它们:
funcPtrArray[0] = //
this
compiles
reinterpret_cast<FuncPtr>(&doSomething);
转换函数指针的代码是不可UL的(C++不保证所有的函数指针都被用一LҎ表示Q,在一些情况下q样的{换会产生不正的l果Q参见条ƾM31Q,所以你应该避免转换函数指针cdQ除非你处于着背水一战和刀架喉的危急时刅R一把锋利的刀。一把非帔R利的刀?br />如果你用的~译器缺乏对新的cd转换方式的支持,你可以用传统的类型{换方法代替static_cast,
const_cast, 以及reinterpret_cast。也可以用下面的宏替换来模拟新的cd转换语法Q?br />#define
static_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define const_cast(TYPE,EXPR)
((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR)
((TYPE)(EXPR))
你可以象q样使用使用Q?br />double result = static_cast(double,
firstNumber)/secondNumber;
update(const_cast(SpecialWidget*,
&sw));
funcPtrArray[0] = reinterpret_cast(FuncPtr,
&doSomething);
q些模拟不会象真实的操作W一样安全,但是当你的编译器可以支持新的的类型{换时Q它们可以简化你把代码升U的q程?br />?
有一个容易的Ҏ来模拟dynamic_cast的操作,但是很多函数库提供了函数Q安全地在派生类与基cM间进行类型{换。如果你没有q些函数而你有必
进行这Lcd转换Q你也可以回到C风格的类型{换方法上Q但是这L话你不能获知类型{换是否失败。当Ӟ你也可以定义一个宏来模?
dynamic_cast的功能,p模拟其它的类型{换一P
#define
dynamic_cast(TYPE,EXPR)
(TYPE)(EXPR)
误住,q个模拟q不能完全实现dynamic_cast的功能,它没有办法知道{换是否失败?br />?
知道Q是的,我知道,新的cd转换操作W不是很观而且用键盘键入也很麻烦。如果你发现它们看上d在o厌,C风格的类型{换还可以l箋使用q且?
法。然而,正是因ؓ新的cd转换W缺乏美感才能它I补了在含义精性和可L认性上的缺炏Vƈ且,使用新类型{换符的程序更Ҏ被解析(不论是对人工q是
对于工具E序Q,它们允许~译器检出原来不能发现的错误。这些都是放弃C风格cd转换Ҏ的强有力的理由。还有第三个理由Q也许让cd转换W不观和键
入麻烦是一件好事?/font>
stringcȝ字符操作Q?br />
const char &operator[](int n)const;
const char &at(int n)const;
char &operator[](int n);
char &at(int n);
operator[]和at()均返回当前字W串中第n个字W的位置Q但at函数提供范围查,当越界时会抛出out_of_range异常Q下标运符[]不提供检查访问?br />
const char *data()const;//q回一个非nulll止的c字符数组
const char *c_str()const;//q回一个以nulll止的c字符?br />
int copy(char *s, int n, int pos = 0)
const;//把当前串中以pos开始的n个字W拷贝到以sv始位|的字符数组中,q回实际拯的数?/font>
string的特性描q?
int capacity()const; //q回当前定wQ即string中不必增加内存即可存攄元素个数Q?br />
int max_size()const; //q回string对象中可存放的最大字W串的长?br />
int size()const; //q回当前字符串的大小
int length()const; //q回当前字符串的长度
bool empty()const; //当前字符串是否ؓI?br />
void resize(int len,char c);//把字W串当前大小|ؓlenQƈ用字Wc填充不的部?/font>
stringcȝ输入输出操作:
stringc重载运符operator>>用于输入Q同样重载运符operator<<用于输出操作?br />
函数getline(istream &in,string &s);用于从输入流in中读取字W串到s中,以换行符'\n'分开?br />
string的赋|
string &operator=(const string &s);//把字W串s赋给当前字符?br />
string &assign(const char *s);//用ccd字符串s赋?br />
string &assign(const char *s,int n);//用c字符串s开始的n个字W赋?br />
string &assign(const string &s);//把字W串s赋给当前字符?br />
string &assign(int n,char c);//用n个字Wc赋值给当前字符?br />
string &assign(const string &s,int start,int n);//把字W串s中从start开始的n个字W赋l当前字W串
string &assign(const_iterator first,const_itertor
last);//把first和lastq代器之间的部分赋给字符?br />
string的连接:
string &operator+=(const string
&s);//把字W串sq接到当前字W串的结?
string &append(const char *s); //把ccd字符串sq接到当前字W串l尾
string &append(const char *s,int
n);//把ccd字符串s的前n个字W连接到当前字符串结?br />
string &append(const string &s); //同operator+=()
string &append(const string &s,int pos,int n);//把字W串s中从pos开始的n个字W连接到当前字符串的l尾
string &append(int n,char c); //在当前字W串l尾dn个字Wc
string &append(const_iterator first,const_iterator
last);//把P代器first和last之间的部分连接到当前字符串的l尾
string的比较:
bool operator==(const string &s1,const string &s2)const;//比较两个字符串是否相{?br />
q算W?>","<",">=","<=","!="均被重蝲用于字符串的比较Q?br />
int compare(const string &s) const;//比较当前字符串和s的大?br />
int compare(int pos, int n,const string &s)const;//比较当前字符串从pos开始的n个字W组成的字符串与s的大?br />
int compare(int pos, int n,const string &s,int pos2,int
n2)const;//比较当前字符串从pos开始的n个字W组成的字符串与s中pos2开始的n2个字W组成的字符串的大小
int compare(const char *s) const;
int compare(int pos, int n,const char *s) const;
int compare(int pos, int n,const char *s, int pos2) const;
compare函数?gt;时返?Q?lt;时返?1Q?=时返?
string的子Ԍ
string substr(int pos = 0,int n = npos) const;//q回pos开始的n个字W组成的字符?
string的交换:
void swap(string &s2); //交换当前字符串与s2的?/font>
stringcȝ查找函数Q?/font>
int find(char c, int pos = 0)
const;//从pos开始查扑֭Wc在当前字W串的位|?br />
int find(const char *s, int pos = 0) const;//从pos开始查扑֭W串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查扑֭W串s中前n个字W在当前串中的位|?br />
int find(const string &s, int pos = 0) const;//从pos开始查扑֭W串s在当前串中的位置
//查找成功时返回所在位|,p|q回string::npos的?
int rfind(char c, int pos = npos)
const;//从pos开始从后向前查扑֭Wc在当前串中的位置
int rfind(const char *s, int pos = npos) const;
int rfind(const char *s, int pos, int n = npos) const;
int rfind(const string &s,int pos = npos) const;
//从pos开始从后向前查扑֭W串s中前n个字W组成的字符串在当前串中的位|,成功q回所在位|,p|时返回string::npos的?
int find_first_of(char c, int pos = 0)
const;//从pos开始查扑֭WcW一ơ出现的位置
int find_first_of(const char *s, int pos = 0) const;
int find_first_of(const char *s, int pos, int n) const;
int find_first_of(const string &s,int pos = 0) const;
//从pos开始查扑ֽ前串中第一个在s的前n个字W组成的数组里的字符的位|。查扑֤败返回string::npos
int find_first_not_of(char c, int pos = 0) const;
int find_first_not_of(const char *s, int pos = 0) const;
int find_first_not_of(const char *s, int pos,int n) const;
int find_first_not_of(const string &s,int pos = 0) const;
//从当前串中查扄一个不在串s中的字符出现的位|,p|q回string::npos
int find_last_of(char c, int pos = npos) const;
int find_last_of(const char *s, int pos = npos) const;
int find_last_of(const char *s, int pos, int n = npos) const;
int find_last_of(const string &s,int pos = npos) const;
int find_last_not_of(char c, int pos = npos) const;
int find_last_not_of(const char *s, int pos = npos) const;
int find_last_not_of(const char *s, int pos, int n) const;
int find_last_not_of(const string &s,int pos = npos) const;
//find_last_of和find_last_not_of与find_first_of和find_first_not_of怼Q只不过是从后向前查?
stringcȝ替换函数Q?/font>
string &replace(int p0, int n0,const char
*s);//删除从p0开始的n0个字W,然后在p0处插入串s
string &replace(int p0, int n0,const char *s, int
n);//删除p0开始的n0个字W,然后在p0处插入字W串s的前n个字W?br />
string &replace(int p0, int n0,const string &s);//删除从p0开始的n0个字W,然后在p0处插入串s
string &replace(int p0, int n0,const string &s, int pos, int
n);//删除p0开始的n0个字W,然后在p0处插入串s中从pos开始的n个字W?br />
string &replace(int p0, int n0,int n, char c);//删除p0开始的n0个字W,然后在p0处插入n个字Wc
string &replace(iterator first0, iterator last0,const char
*s);//把[first0Qlast0Q之间的部分替换为字W串s
string &replace(iterator first0, iterator last0,const char *s, int
n);//把[first0Qlast0Q之间的部分替换为s的前n个字W?br />
string &replace(iterator first0, iterator last0,const string
&s);//把[first0Qlast0Q之间的部分替换Zs
string &replace(iterator first0, iterator last0,int n, char
c);//把[first0Qlast0Q之间的部分替换为n个字Wc
string &replace(iterator first0, iterator last0,const_iterator first,
const_iterator last);//把[first0Qlast0Q之间的部分替换成[firstQlastQ之间的字符?
stringcȝ插入函数Q?/font>
string &insert(int p0, const char *s);
string &insert(int p0, const char *s, int n);
string &insert(int p0,const string &s);
string &insert(int p0,const string &s, int pos, int n);
//?个函数在p0位置插入字符串s中pos开始的前n个字W?br />
string &insert(int p0, int n, char c);//此函数在p0处插入n个字Wc
iterator insert(iterator it, char c);//在it处插入字WcQ返回插入后q代器的位置
void insert(iterator it, const_iterator first, const_iterator last);//在it处插入[firstQlastQ之间的字符
void insert(iterator it, int n, char c);//在it处插入n个字Wc
stringcȝ删除函数
iterator erase(iterator first, iterator last);//删除[firstQlastQ之间的所有字W,q回删除后P代器的位|?br /> iterator erase(iterator it);//删除it指向的字W,q回删除后P代器的位|?br /> string &erase(int pos = 0, int n = npos);//删除pos开始的n个字W,q回修改后的字符?/font>
stringcȝq代器处理:
stringcL供了向前和向后遍历的q代器iteratorQP代器提供了访问各个字W的语法Q类g指针操作QP代器不检查范围?br />
用string::iterator或string::const_iterator声明q代器变量,const_iterator不允许改变P代的内容。常用P代器函数有:
const_iterator begin()const;
iterator begin();
//q回string的v始位|?br />
const_iterator end()const;
iterator end();
//q回string的最后一个字W后面的位置
const_iterator rbegin()const;
iterator rbegin();
//q回string的最后一个字W的位置
const_iterator rend()const;
iterator rend();
//q回stringW一个字W位|的前面
rbegin和rend用于从后向前的P代访问,通过讄q代器string::reverse_iterator,string::const_reverse_iterator实现
字符串流处理Q?/font>
通过定义ostringstream和istringstream变量实现Q?lt;sstream>头文件中
例如Q?br />
string input("hello,this is a test");
istringstream is(input);
string s1,s2,s3,s4;
is>>s1>>s2>>s3>>s4;//s1="hello,this",s2="is",s3="a",s4="test"
ostringstream os;
os<<s1<<s2<<s3<<s4;
cout<<os.str();
模板cd模板函数都是非常有用的工兗例如sqr()函数可以计算qx敎ͼM定义了乘法运的数据cdQ数字,矩阵Q都适用。标准容器类(? list)都是模板Q这样对于每个新cd无需重写了,q正是用旧版的C++时真正头疼的事情Q因此我认ؓISO的标准是个伟大的q步。然而,在这个过E? 中有些东西用得过头了?
例如Q标准库中得string 和iostream 都是使用"character traits"cd作ؓ参数。这意味着同一个basic_string<>cd以用于ASCII字符Ԍ也可用于UnicodeQ甚至用于火 星h的三字节字符Ԍ原则虽然如此Q但许多版本都只是实CASCII字符Ԍ看v来有ҎE)。标准要求这些常用类必须实现成模板Ş式,而这些类几乎? 及到所有C++应用?
但是q对性能和调试会带来许多ȝ。下面几个试验解释了q个问题Q本试验使用的编译器为VC++6.0)。编译器同时支持新风格的 iostreamQ用模板)和经兔R格的iostream, 因此我们能比较他们二者的版本实现。第一个测试程序当然是使用"Hello, Word"了,新风格的~译旉是经兔R格的2倍。另一个更正规的例子大U有200行,每行输出10个变量用于计数。这个测试程序最显著的结论是~译? 度:新风格版本花?0U编译完成,而旧版本只用了1.5U?0U时间可q不,可以完成很多事情。另外,新风格版本的可执行文件的大小?15KQ? 而旧版本只有70K。你的测试数据可能有些出入,但是整体l论是一LQ当使用新版本时Q会有更慢的~译速度和更大的可执行文件。这q不是因为微软公司编 译器的问题,使用GCC试也会得到同样的结论?
当然Q和q去不一P可执行文件的大小q不是那么重要,现在Q可~程讑֤U类正快速增长,包括许多信息应用Q如遥控、手机、智能冰、基 于蓝牙技术的咖啡机等{,在这些应用中内存q几q都会是十分宝贵的资源。用标准iostream 而生的额外的二q制文gQ来源于内联了整个模板类的代码,要是没有code bload工具Q你很难优化那些重要的操作。对我来_~译旉问题更严重一些,因ؓq样意味着更长的等待,从而失M开发中非常重要原则Q互动原则?
现在我们来考虑调试的问题。标准库中string cȝ模板实现非常聪明Q但q不适合于调试。你会面临用超长名字的~译器和调试器的信息Q?
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char>>
当然Q在许多应用中我们都需要这Ustd::string提供的灵zL,例如Q需要同时处理ASCII 和Unicode字符Ԍ或者需要定制自qallocator {等。但qƈ不是普遍需?通常E序员要么只处理ASCIIQ要么只处理Unicode ), 看v来对于程序员承担q种范型机制有些不公q뀂这U机制确实让标准库的设计者觉得很有意思,但增加了应用开发程序员使用的复杂度。这g颠倒了q个原则Q? 良好的标准库设计应该隐藏其实现的复杂度,而让用户直接使用。但std::string 对其实现的复杂度隐藏得ƈ不够Q导致在用户使用q程中不断的遇到设计中的问题。我们不能要求标准库的用户都是专家。标准坚持要求这U特定的实现方式Q和? 准库的设计初Lq背Q其初衷是只提供公共的接口和包含一些特定功能的cd。自Ӟq种范型模板对于那些真正去要他们的h是一直有效的?
q些l节考虑同样应用于标准容器,例如list<>容器Qlist 有一些额外的默认模板参数Q用于定义了默认的allocator。当然自己定义allocator 十分有用Q但l大多数Z需要自己去实现。这些泛化的版本完全可以作ؓ单独的模板提供。我承认q样做会让标准库的设计在技术上变得没有以前有意思,但这? 库在设计之初应该考虑到最l用戗篡改一下C++的颂歌:用户不应该ؓ他们不需要的东西买单?
当我们不需要模板的时候,我们不得不用模ѝ除此之外,在CQ+中用范型~程q会遇到另一个的问题。大多数人都同意Q标准的algorithm 十分有用。如果你有一个整型的vector, 你可以直接用下面的语句来排序:
sort(v.begin(),v.end());
但有些应用理解v来十分晦涩:
copy_if(v.begin(),v.end(),ostream_iterator<int>(cout) bind2nd(greater<int>(),7));
vector<int>::iterator li;
for (li = v.begin(); li != v.end(); ++li)
if (*li > 7) cout << *li;
for_each(ls.begin(),ls.end(),
bind2nd(mem_fun(&Shape::draw),canvas));
ShapeList::iterator li;
for (li = ls.begin(); li != ls.end(); ++li)
(*li)->draw(canvas);
for_each(ls.begin(),ls.end(),
void lambda(Shape *p) { p->draw(canvas); });
C++ 是一U不可思议的编E语aQ小到手机,大到跨国际网l,都有其应用。它非常灉|Q能够支持多U编E风|但这U灵zd样也是其问题所在。编E的艺术在于? 特定的问题选择合适编E风|像老师L醒写作文是要选择好的风格一栗我q不惌?C++ 标准库,q里面包含了许多人的辛勤力_Qƈ为大家提供了一个公共^台。我对于q个标准的态度是,它和范型~程联系q于紧密Q从而变成了在说明什么风格是? 的编E风?例如Q算法中明显們于不要用显式@?, 同时它也让程序员们不得不介入一些实现细?如basic_string<>)Q这样做让h们更加觉得C++ 是只是内核工E师们的~程语言?
函数原型 | 函数描述 |
void *memcpy(void *s1,const void *s2, size_t n) | ?/span>s2所指的对象中的n个字W复制到s1所指的对象中。返?/span>s1l果指针 |
void *memmove(void *s1,const void *s2,size_t n) | ?/span>memcpy,q且多考虑了重叠情况(Overlapping BuffersQ?/span> |
int memcmp(const void *s1,const void *s2,size_t n) | s1?/span>s2指向对象的前n个字W。如?/span>s1所指向对象的字W等于、小于或大于s2所指向对象中的字符Q返回值分别等?/span>0?/span><0 ?/span>>0 |
void *memchr(const char *s,int c,size_t n) | 定位s的前n个字W首ơ出?/span>c的位|。找到就q回指向它的指针Q否则返?/span>0 |
void *memset(void *s, int c,size_t n) | ?/span>c复制?/span>s所指的对象的前n个字W中。返回指向结果指?/span> |
函数原型 | 函数描述 |
char *strchr(const char *s,int c) | 查找c?/span>s中首ơ出现的位置Q成功将q回该位|的指针Q否则返?/span>NULL |
size_t strcspn(const char *s1,const char *s2) | 计算q返?/span>s1中不包含s2中Q何字W的起始D늚长度。即?/span>s1中查找是否有s2的字W,若碰到有该字W则q回从开始(数数1开始)到该字符之前的字W串长度?/span> |
size_t strspn(const char *s1,const char *s2) | 计算q返?/span>s1中只包含s2中字W的起始D长度。即当在s1中没遇到?/span>s2中的字符Ӟq回从开始到该字W之前的字符串的长度?/span> |
char *strpbrk(const char *s1,const char *s2) | 定位字符?/span>s1首次出现在字W串s2中字W的位置。若扑ֈ了字W串s2中的字符Q返回一个指向字W串s1中该字符的指针,否则q回NULL |
char *strrchr(const char *s,int c) | q回c?/span>s中最后一ơ出现的位置指针Q否则返?/span>NULL |
char *strstr(const char *s1, const char *s2) | q回s2?/span>s1中首ơ出玎ͼ整个字符串匹配)的位|指针,否则q回NULL |
函数原型 | 函数描述 |
double atof(const char *nPtr) | 把字W串nPtr转换?span lang="EN-US">doublecd |
int atoi(const char* nPtr) | 把字W串nPtr转换?span lang="EN-US">intcd |
long atol(const char *nPtr) | 把字W串nPtr转换?span lang="EN-US">long |
double strtod(const char* nPtr,char **endPtr) | 把字W串nPtr转换?span lang="EN-US">doublecdQ?span lang="EN-US">endPtr指向nPtr中第一个不是数字的字符的位|?/span> |
long strtol(const char *nPtr,char** endPtr, int base) | 把字W串nPtr转换?span lang="EN-US">longcdQ?span lang="EN-US">eendPtr指向nPtr中第一个不是数字的字符的位|?/span>Q?span lang="EN-US">base是待转换字符串中数值的q制cdQ?span lang="EN-US">0表示可以是(8?span lang="EN-US">10?span lang="EN-US">16Q。也可是2?span lang="EN-US">36中Q何倹{?/td> |
unsigned long strtoul(const char* nPtr,char **endPtr, int base) | 把字W串nPtr转换?span lang="EN-US">unsigned longcdQ?span lang="EN-US">endPtr指向nPtr中第一个不是数字的字符的位|?/span>Q?span lang="EN-US">base是待转换字符串中数值的q制cdQ?span lang="EN-US">0表示可以是(8?span lang="EN-US">10?span lang="EN-US">16Q。也可是2?span lang="EN-US">36中Q何倹{?/td> |
函数原型 | 函数描述 |
int isdigit(int c) | 如果 c 是一个数字,q回 true Q否则返?/span> false |
int isalpha(int c) | 如果 c 是一个字母,q回 true Q否则返?/span> false |
int isalnum(int c) | 如果 c 是一个字母或数字Q返?/span> true Q否则返?/span> false |
int isxdigit(int c) |
如果 c 是一个十六进制字W,q回 true Q否则返?/span> false |
int islower(int c) | 如果 c 是一个小写字母,q回 true Q否则返?/span> false |
int isupper(int c) |
如果 c 是一个大写字母,q回 true Q否则返?/span> false |
int tolower(int c) |
如果c为大写字母,q回其小写字母,否则q回原参?/span> |
int toupper(int c) |
如果c为小写字母, q回其大写字母,否则q回原参?/span> |
int isspace(int c) | 如果 c 是一个空白符Q返?/span> true Q否则返?/span> false 。空白符包括Q?/span> ’\n? I格Q?/span> ’\t? , ‘\r?, q纸W( ’\f?/span> Q?/span> , 垂直制表W?/span> (‘\v? |
int iscntrl(int c) | 如果 c 是一个控制符Q返?/span> true Q否则返?/span> false |
int ispunct(int c) |
如果 c 是一个除I格、数字和字母外的可打印字W,q回 true,否则q回false |
int isprint(int c) |
如果 c 是一个可打印W(包括I格Q,q回 true Q否则返?/span> false |
int isgraph(int c) |
如果 c 是除I格之外的可打印字符Q返?/span> true Q否则返?/span> false |
在前面,输入Q输出的数据没有指定格式Q它们都按缺省的格式输入Q输出。然而,有时需要对数据
格式q行控制。这旉利用ioscM定义的格式控制成员函敎ͼ通过调用它们来完成格式的讄。ioscȝ格式控制函数如下所C:
long flags( ) const | q回当前的格式标志?/font> |
long flays(long newflag) | 讄格式标志为newflagQ返回旧的格式标志?/font> |
long setf(long bits) | 讄指定的格式标志位Q返回旧的格式标志?/font> |
long setf(long bits,long field) | field指定的格式标志位|ؓbitsQ返回旧的格式标志?/font> |
long unsetf(long bits) | 清除bits指定的格式标志位Q返回旧的格式标志?/font> |
long fill(char c) | 讄填充字符Q缺省条件下是空根{?/font> |
char fill( ) | q回当前填充字符?/font> |
int precision(int val) | 讄_度ؓvalQ控制输出QҎ的有效位Q返回旧倹{?/font> |
int precision( ) | q回旧的_度倹{?/font> |
int width(int val) | 讄昄数据的宽?域宽),q回旧的域宽?/font> |
int width( ) | 只返回当前域宽,~省宽度?。这时插入操作能按表C数 据的最宽度显C数据。?/font> |
预定义的操纵子
使用成员函数控制格式化输入输出时Q每个函数调用需要写一条语句,其是它不能用在插入或提取运符的表辑ּ中,而用操U늮子,则可以在插入和提取运符的表辑ּ中控制格式化?/font>
入和输出。在E序中用操U늮字必d入头文g
iomanip.h
dec | 十进制的输入输出 |
hex | 十六q制的输入输?/font> |
oct | 八进制的输入输出 |
ws | 提取I白字符 |
ends | 输出一个nul字符 |
endl | 输出一个换行字W,同时h?/font> |
flush | h?/font> |
resetiosflags(long) | 请除特定的格式标志位 |
setiosflags(long) | 讄特定的格式标志位 |
setfill(char) | 讄填充字符 |
setprecision(int) | 讄输出点数的_?/font> |
setw(int) | 讄域宽格式变量 |
错误处理
在对一个流对象q行I/O操作Ӟ可能会生错误。当错误发生Ӟ错误的性质被记录在ioscȝ一个数据成员中?/font>
ioscM定义的描q错误状态的帔R:
goodbit | 没有错误Q正常状态 eofbit 到达的l尾 |
failbit | I/O操作p|Q清除状态字后,可以Ҏl箋q行操作?/font> |
badbit | 试图q行非法操作Q清除状态字后,可能还可以使用?/font> |
hardfail | 致命错误Q不可恢复的错误?/font> |
q回cd | ioscȝ成员 | 描 q?/font> |
ostream* | tie(ostream*) | 当前流与指定的输出连接v来。每当需?d当前时Q连接的会自动h。C++库已用cin.tie(cout)输入流与输出流q接 h。要取消与输出流的连接可采用is.tie(0) |
ostream* | tie( ) | q回指向q接的指针 |
q回cd | ostreamcȝ成员 | 描 q?/font> |
ostream& | put(char ch) | 向流中输Z个字WchQ不q行M转换 |
ostream& | write(char*,int) | 向流中输出指定长度的字符Ԍ不进行{?/font> |
ostream& | flush( ) | h,输出所有缓冲的但还未输出的数据 |
ostream& | seekp(streampos) | Ud的当前指针到给定的l对位置 |
ostream& | seekp(sereamoff,seek_dir) | 的当前指针cM与文件的当前指针 |
streampos | teelp( ) | q回的当前指针的绝对位|?/font> |
istreamcȝ成员函数
q回cd | istreamcȝ成员 | 描 q?/font> |
int | get( ) | dq返回一个字W?/font> |
istream& | get(char&c) | d字符q存入c?/font> |
istream& | get(char*ptr,int len,char delim='') | d指定的字W到~冲ZQ直到遇到指定的分界Wؓ止,分界W不填入~冲区?/font> |
istream& | getline(char*ptr,int len,char delim='') | 与get(char*ptr,int len,chardelim =''Q?cMQ但分界符?/font> 入缓冲区?/font> |
istream& | putback( ) | 最q读取的字符攑֛中 |
istream& |
read(char*,int) |
d规定长度的字W串到缓冲区?br />函数gcount()q回d的字节数 |
int | peek( ) | q回中下一个字W,但不Ud?/font> 件指?/font> |
istream& | seekg(streampos) | Ud当前指针Cl对地址 |
istream& | seekg(streampos,seek_dir) | Ud当前指针C相对地址 |
streampos | tellg( ) | q回当前指针 |
istream& | ignore(int n=1,delim=EOF) | 跌中几个字符Q或直到遇到指定的分界符为止 |