??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
FQ宏定义也可以实现重用,Z么不使用宏?
QQ宏避开C++cd查机Ӟ两个不同cd参数之间的比较将会导致错误?/span>
模板Q模板函敎ͼ模板cd对象之间的关pR?/span>
模板 Q函数模板和cL板) 模板函数 模板c?/span> 对象
模板 Q函数模板和cL板) 模板函数 模板c?/span> 对象
例如Q?/span>
模板形参?/span>T实例化的参数?/span>
函数模板是一个不完全的函敎ͼ首先需要将模板形参T实例化ؓ定的类型。这个类型可以是预定义的Q也可以是用戯定义?/span>
模板函数的异?/span>
1 先调用顺序遵循什么约定?
Q?/span>1Q寻扄型完全匹配的函数?/span>
Q?/span>2Q寻扑և数模板,实例化后Q生匹配的模板函数?/span>
Q?/span>3Q若Q?/span>1Q(2Q失败,试低一U的对函数重载的Ҏ。例如,通过cd转换?/span>
Q?/span>4Q(1Q(2Q(3Q都为匹配,则是一个错误?/span>
2 先了解了q样的约定,很Ҏ理解以下Q?/span>
用非模板函数重蝲函数模板Q只声明非模板类函数的原型,不给出函CQ而函C借用函数模板的函C?/span>
例如Q?/span>
template <class T>
T max( T x, T y)
{
return( x > y )? x : y;
}
int max( int, int);
//int max 重蝲了函数模板的函数体;
int i;
char c;
maxQ?/span>i, cQ?/span>;
3 定义一个完整的有函C的非模板函数?/span>
比如Q?/span>max(char, char)Ӟ上面的函数模板不能比较比一个字W更长的字符串了?/span>
所以,像一般的重蝲函数一样定义?/span>
char *max( char *x, char *y )
{
return ( strcmp(x, y) > 0 ) ? x Q?/span>yQ?/span>
}
char * max重蝲了函数模板,当调用ؓQ?/span>
max ( “abcd”, “efgh” ); 执行此函数?/span>
class A
{ …
private:
A(const A &a); // U有的拷贝构造函?span lang=EN-US>
A & operate =(const A &a); // U有的赋值函?span lang=EN-US>
};
如果有h试图~写如下E序Q?/span>
A b(a); // 调用了私有的拯构造函?span lang=EN-US>
b = a; // 调用了私有的赋值函?span lang=EN-US>
~译器将指出错误Q因为外界不可以操作A的私有函数?/span>
但是怎样才能使用构造拷贝和赋值函数呢Q?/span>
虚拟函数使用Q?/span>C++exams\destructor
在编写派生类的赋值函数时Q注意不要忘记对基类的数据成员重新赋倹{例如:
析构函数
构造函数初始化表:构造函数特D的初始化方?#8220;初始化表辑ּ?#8221;Q简U初始化表)?/p>
初始化表位于函数参数表之后,却在函数?{} 之前。这说明该表里的初始化工作发生在函数体内的Q何代码被执行之前?/p>
规则
u 如果cd在承关p,zcdd其初始化表里调用基类的构造函数?/p>
u cȝconst帔R只能在初始化表里被初始化Q因为它不能在函C内用赋值的方式来初始化?/p>
u cȝ数据成员的初始化可以采用初始化表或函C内赋gU方式,q两U方式的效率不完全相同?/p>
效率
1 内部成员Q?/p>
初始化表和函C内赋值都可以Q但效率不完全相同,但后者更为清晰直观?/p>
例子Q?/p>
class F
{
public:
F(int x, int y); // 构造函?/p>
private:
int m_x, m_y;
int m_i, m_j;
}
F::F(int x, int y) : m_x(x), m_y(y) { m_i = 0; m_j = 0; } |
F::F(int x, int y) { m_x = x; m_y = y; m_i = 0; m_j = 0; } |
CZ9-2(c) 数据成员在初始化表中被初始化 CZ9-2(d) 数据成员在函C内被初始?/p>
两种方式效率区别不大?/p>
2 非内部成员:
只能用初始化表,提高效率?/p>
例子Q?/p>
class A
{…
A(void); // 无参数构造函?/p>
A(const A &other); // 拯构造函?/p>
A & operate =( const A &other); // 赋值函?/p>
}Q?/p>
class B
{
public:
B(const A &a); // B的构造函?/p>
private:
A m_a; // 成员对象
};
比较与分析:
B::B(const A &a) : m_a(a) { … } |
B::B(const A &a) { m_a = a; … } |
1 BcL造函数的初始化里Q调用了Acȝ拯构造函数?/p>
2 BcL造初始化里,隐藏了以下几个步骤:
先创Za对象Q调用了Acȝ无参数构造函敎ͼ
把a赋值给m_aQ调用了Acȝ赋值函敎ͼ
深入探讨Q?/p>
构造和析构的次序?
构造从最深处的基cd始的Q先一层层调用基类的构造函敎ͼ然后调用成员对象的构造函数?/p>
而析构函C格按照构造函数相反的ơ序执行Q该ơ序唯一Q以便让~译器自动执行析构函数?/p>
特别之处是,成员对象初始化次序不受构造函数初始化表次序媄响,由在cM声明的次序决定。而类声明是唯一的,构造函数却可能有多个,所以有多个不同ơ序函数初始化表。如果按照构造函数的ơ序构造,那么解析函数不能得到唯一的逆序?/p>
?/span>C++中,用内联取代所有宏Q但?/span>Debug版本中,assert是例外?/span>
assert不生Q何副作用Q调用函C引v内存、代码的变动Q所?/span>assert是宏?/span>
内联格式Q?/span>
void Foo(int x, int y);
inline void Foo(int x, int y) // inline与函数定义体攑֜一?span lang=EN-US>
{
…
}
用于实现的关键字Q而非用于声明的关键字?/span>
在类中的规范格式Q?/span>
class A
{
public:
void Foo(int x, int y)Q?span lang=EN-US>
}
// 定义文g
inline void A::Foo(int x, int y)
{
…
}
什么情况不适合使用内联Q?/span>
1 内联函数代码较长Q导致内存消耗较高?/span>
2 内联函数包括循环Q执行内联函C码比调用函数的开销大?/span>
FQؓ什么构造和解析函数不用内联?
QQ因为析构函数可?#8220;隐藏”一些行为,例如执行基类或成员对象的析构q程?/span>
有时候编译器会自动取消一些不值得的内联,所以在声明时不?/span>inline是合理的?/span>
提高E序易用性,避免每次调用函数参数都相同的情况?/span>
~省值只能出现在函数声明中,不能在函数定义中?/span>
两个原因Q?/span>
一是函数的实现Q定义)本来׃参数是否有缺省值无养I所以没有必要让~省值出现在函数的定义体中?/span>
二是参数的缺省值可能会改动Q显然修改函数的声明比修改函数的定义要方ѝ?/span>
规则Q参C后向前缺?/span>
正确的示例如下:
void Foo(int x, int y=0, int z=0);
错误的示例如下:
void Foo(int x=0, int y, int z=0);
但要避免二义?/span>
q算W重载及其规?/span>
q算W即可定义ؓ成员函数Q也可定义ؓ全局函数?/span>
规则如下Q?/span>
q算W?span lang=EN-US> |
规则 |
所有的一元运符 |
重蝲为成员函?span lang=EN-US> |
= () [] -> |
只能重蝲为成员函?span lang=EN-US> |
+= -= /= *= &= |= ~= %= >>= <<= |
重蝲为成员函?span lang=EN-US> |
所有其它运符 |
重蝲为全局函数 |
不能重蝲的运符
Q?span lang=EN-US>1Q不能改?span lang=EN-US>C++内部数据cdQ如int,float{)的运符?span lang=EN-US>
Q?span lang=EN-US>2Q不能重?span lang=EN-US>‘.’Q因?span lang=EN-US>‘.’在类中对M成员都有意义Q已l成为标准用法?span lang=EN-US>
Q?span lang=EN-US>3Q不能重载目?span lang=EN-US>C++q算W集合中没有的符P?span lang=EN-US>#,@,${。原因有两点Q一是难以理解,二是难以定优先U?span lang=EN-US>
Q?span lang=EN-US>4Q对已经存在的运符q行重蝲Ӟ不能改变优先U规则,否则引h乱?span lang=EN-US>
函数内联
目的Q提高执行效率?/span>
?/span>
QZ么要用?
A提高执行效率?/span>
Q如何提高Q?/span>
A宏代码本w不是函敎ͼ但用v来象函数。预处理器用复制宏代码的方式代替函数调用Q省M参数压栈、生成汇~语a?/span>CALL调用、返回参数、执?/span>return{过E,从而提高了速度?/span>
Q~点Q?/span>
使用宏代码最大的~点是容易出错,预处理器在复制宏代码时常生意想不到的辚w效应?/span>
例如Q?/span>
#define MAX(a, b) (a) > (b) ? (a) : (b)
语句
result = MAX(i, j) + 2 ;
被预处理器解释?span lang=EN-US>
result = (i) > (j) ? (i) : (j) + 2 ;
׃q算W?span lang=EN-US>‘+’比运符‘:’的优先高,所以上q语句ƈ不等价于期望?span lang=EN-US>
result = ( (i) > (j) ? (i) : (j) ) + 2 ;
如果把宏代码改写?span lang=EN-US>
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
则可以解决由优先U引L错误。但是即使用修改后的宏代码也不是万无一qQ例如语?span lang=EN-US style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 0cm; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt">result = MAX(i++, j);
被预处理器解释?span lang=EN-US>
result = (i++) > (j) ? (i++) : (j);
对于C++ 而言Q用宏代码q有另一U缺点:无法操作cȝU有数据成员?span lang=EN-US>
“隐藏”是指zcȝ函数屏蔽了与其同名的基类函数Q规则如下:
Q?span lang=EN-US>1Q如果派生类的函C基类的函数同名,但是参数不同。此Ӟ不论有无virtual关键字,基类的函数将被隐藏(注意别与重蝲hQ?span lang=EN-US>
Q?span lang=EN-US>2Q如果派生类的函C基类的函数同名,q且参数也相?/u>Q但是基cd数没?span lang=EN-US>virtual关键字。此Ӟ基类的函数被隐藏Q注意别与覆盖淆)?span lang=EN-US>
回顾Q?/span>
静态多态性:函数重蝲Q运符重蝲Q?/span>
动态多态性:l承Q虚函数Q?/span>
上述例子之所以用指针目的是Q表达一U动态性质Q即当指针指向不同对象可以调用不同方法。但事实上不能达到这L效果。虽然可以强制指针的cd转换Q(derived *Q?/span>p->g();Q,或者直接调用指向派生类的指针,但是只要成员函数声明ؓvirtualp起到q样的作用?/span>
隐藏
不同指针指向同一地址Q那么指针的cd军_调用ҎQ还是指针指向的对象Q?/span>
例子Q?/span>C++exams\hide
可以看出Q?/span>virtual函数可以实现不同cd的指针指向不同对象,调用不同的方法?/span>
是_当父cd象和子类对象同时存在Q且调用父子对象的同名方法时Q只能用虚拟函数实现?/span>
另外Q提供一U思\Q?/span>
如果基类与派生类的同名函数参C同,比如Q?/span>
class Base { public: void f(int x); }; |
class Derived : public Base { public: void f(char *str); }; |
void Test(void) { Derived *pd = new Derived; pd->f(10); // error } |
基类?/span>int,zcMؓstring.如果惛_zcM调用基类的方法,卛_zcMd基类Ҏ的调用?/span>
class Derived : public Base
{
public:
void f(char *str);
void f(int x) { Base::f(x); }
};
但此做法Ҏ混ؕQ不宜用。最好的Ҏq是?/span>virtual?/span>
指针cd
对象cd
面向对象动态的设计Q应该是对象cd起到关键作用Q而不是指针类型?/span>
成员函数被重载的特征Q?span lang=EN-US>
Q?span lang=EN-US>1Q相同的范围Q在同一个类中)Q?span lang=EN-US>
Q?span lang=EN-US>2Q函数名字相同;
Q?span lang=EN-US>3Q参C同;
Q?span lang=EN-US>4Q?span lang=EN-US>virtual关键字可有可无?span lang=EN-US>
覆盖是指zcd数覆盖基cd敎ͼ特征是:
Q?span lang=EN-US>1Q不同的范围Q分别位于派生类与基c)Q?span lang=EN-US>
Q?span lang=EN-US>2Q函数名字相同;
Q?span lang=EN-US>3Q参数相同;
Q?span lang=EN-US>4Q基cd数必Lvirtual关键字?span lang=EN-US>
例子Q?/span>C++exams\cover
指向基类的指针可以指向其共有zcȝ对象Q但反之不行Q另外私有派生类也不行?/span>
比如QR 是基c,汽车,马R {是zcR?/span>
一个Rcȝ指针可以指向Mzc,因ؓ它们都属于R?/span>
而反之,不能说指向马车的指针可以指向车,因ؓ车的概念最大?/span>
|
重蝲 |
内联 |
const |
virtual |
全局函数 |
√ |
√ |
|
|
cȝ成员函数 |
√ |
√ |
√ |
√ |
内部标识W?/span>
~译器根据参Cؓ每个重蝲函数创徏内部标识W,以便区分忽略q回g有返回值的重蝲函数?/span>
q接交换指定W号
C~译q的函数Q经q编译器标识后与C++的表C风g同。所?/span>C++不能直接调用C~译出的函数?/span>C++提供extern “C”
例如Q?/span>
extern “C”
{
void foo(int x, int y);
… // 其它函数
}
或者写?/span>
extern “C”
{
#include “myheader.h”
… // 其它C头文?span lang=EN-US>
}
全局函数与成员函数同?/span>
全局函数与成员函数同名不重载,因ؓ函数作用域不同?/span>
Z区别Q调用全局函数Ӟ注意格式Q?/span>
Q:函数名(参数Q;
隐式cd转换D重蝲函数产生二义?/span>
隐式cd转换Q数字本w没有类型,把数字当作参敎ͼ自动q行cd转换?span lang=EN-US>
例如Q?span lang=EN-US>
void output( int x); // 函数声明
void output( float x); // 函数声明
output(0.5)生编译错误,因ؓ~译器不知道该将0.5转换?span lang=EN-US>intq是floatcd的参数?/span>
正确写法Q?/span>
output(int(0.5)); // output int 0
output(float(0.5)); // output float 0.5
只是把指针所指的内存l释放掉Q但q没有把指针本nq掉?/span>
注意Q?/span>
Q?/span>1Q指针消亡了Qƈ不表C它所指的内存会被自动释放?/span>
Q?/span>2Q内存被释放了,q不表示指针会消亡或者成?/span>NULL指针?/span>
比较
malloc?/span>freeQ无法满_态对象的要求。对象在创徏同时q行构造,消亡同时析构?/span>
malloc free是库函数Q不是运符Q不受编译器控制?/span>
new?/span>deleteQ不是库函数Q能动态内存分配和清理内存?/span>
内存耗尽
内存耗尽?/span>new?/span>mallocq回NULL。但?/span>WIN32下,使用虚拟内存Q不会耗尽的?/span>
处理内存耗尽大概有两U?/span>
A *a = new A;
1 一处内存申?/span>
if(a == NULL)
{
return;
}
2 如果有多处申请内存,?span lang=EN-US>
if(a == NULL)
{
cout << “Memory Exhausted” << endl;
exit(1);
}
new?/span>delete使用要点
1 new内置?/span>sizeof、类型{换和cd安全查功能?/span>
2 new在创建非内部数据cd的动态对象的同时完成了初始化工作?/span>
3 对象有多个构造函敎ͼ那么new的语句也可以有多UŞ式?/span>
例如Q?/span>diary files\obj.txt
4 如果?span lang=EN-US>new创徏对象数组Q那么只能用对象的无参数构造函数。例?span lang=EN-US>
Obj *objects = new Obj[100]; // 创徏100个动态对?span lang=EN-US>
不能写成
Obj *objects = new Obj[100](1);// 创徏100个动态对象的同时赋初?span lang=EN-US>1
5在用delete释放对象数组Ӟ留意不要丢了W号‘[]’。例?span lang=EN-US>
delete []objects; // 正确的用?span lang=EN-US>
delete objects; // 错误的用?span lang=EN-US>
后者相当于delete objects[0]Q漏掉了另外99个对象?span lang=EN-US>
指针可以随时指向Lcd的内存块Q它的特征是“可变”Q所以我们常用指针来操作动态内存。指针远比数l灵z,但也更危险?/p>
1 修改内容
char a[] = "hello";
a[0] = 'X';
cout << a << endl;
char *p = "world"; // 注意p指向帔R字符?/p>
p[0] = 'X'; // ~译器不能发现该错误
cout << p << endl;
2 复制与比?/p>
strcpyQnew type[strlenQaQ?1]Q,而不能用p = a //把a的地址l了pQ而不是a的内?/p>
strcmpQif strcmpQa,pQ?= 0Q,而不能用 if( a = p)
// 数组…
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能?nbsp; b = a;
if(strcmp(b, a) == 0) // 不能?nbsp; if (b == a)
…
// 指针…
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); // 不要?p = a;
if(strcmp(p, a) == 0) // 不要?if (p == a)
…
sizeof内存定w计算
1 sizeof(p)相当于sizeof(char*)QC++不支持对指针所指内容容量的计算?/p>
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12字节
cout<< sizeof(p) << endl; // 4字节
2 数组作ؓ函数参数Q退化成同类型指针?/p>
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4字节而不?00字节
}
指针参数传递内?/p>
首先Q考虑函数为参数创Z时副本的问题。对于g递,有Ş参和实参的区别。但对于引用和指针传递,则可能会产生问题?/p>
指针作ؓ函数参数Q不能动态申请内存?/p>
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然?NULL
strcpy(str, "hello"); // q行错误
}
毛病出在函数GetMemory中。编译器L要ؓ函数的每个参数制作时副本,指针参数p的副本是 _pQ编译器?_p = p。如果函C内的E序修改了_p的内容,导致参数p的内容作相应的修攏V这是指针可以用作输出参数的原因。在本例中,_p甌了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemoryq不能输ZQ何东ѝ事实上Q每执行一ơGetMemory׃泄露一块内存,因ؓ没有用free释放内存?o:p>
如何ҎQ?o:p>
1 ?#8220;指向指针的指?#8221;
void GetMemory2(char **p, int num) //**p
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数?&strQ而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
2 用函数返回值来传递动态内?o:p>
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
注意
不要用return语句q回指向“栈内?#8221;的指?/p>
char *GetString(void)
{
char p[] = "hello world";
return p; // ~译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内Ҏ垃圾
cout<< str << endl;
}
1 静态存储区?nbsp; 全局变量 static
2 ?nbsp; 局部变?/p>
3 动态存储区域(堆)(malloc free) (new delete)
常见错误和对{?/p>
1 内存分配未成功?/p>
对策Q程序入口处Q检查指针是否ؓNULL?/p>
Q?Q参?p所指向的内存,用assert( p != NULL)
Q?Qmalloc或new甌的内存,用if ( p != NULL)
2 内存未初始化?/p>
3 内存操作界?/p>
4 忘记释放内存Q内存泄霌Ӏ?/p>
new与delete配对?/p>
5 释放内存Q却l箋使用?/p>
Q?Qreturn不能q回“栈内存指?#8221;?#8220;引用”Q因内存在函数结束时被销毁?/p>
Q?Q释攑ֆ存后Q设为NULLQ防?#8220;野指?#8221;?/p>
规则
1 甌内存后检查。assert( p != NULL)
2 数组、动态内存初始化?/p>
3 甌释放要配寏V?/p>
4 释放内存讄NULLQ防止生野指针?/p>
C++语言中,函数的参数和q回值的传递方式有三种Qg递、指针传递和引用传递?o:p>
g?o:p>
函数内的形参是实参(外部变量Q的一个拷贝,所以不会媄响实参(外部变量Q的倹{?/p>
void Func1(int x)
{
x = x + 10;
}
…
int n = 0;
Func1(n);
cout << “n = ” << n << endl; // n = 0
指针传?o:p>
void Func2(int *x)
{
(* x) = (* x) + 10;
}
…
int n = 0;
Func2(&n);
cout << “n = ” << n << endl; // n = 10
引用传?o:p>
void Func3(int &x)
{
x = x + 10;
}
…
int n = 0;
Func3(n);
cout << “n = ” << n << endl; // n = 10
指针功能强大Q但非常危险。恰如其分的使用“引用”Q发挥其作用?/p>
一般先考虑“引用”Q如?#8220;引用”不能做的事,则再?#8220;指针”来完成?/p>
pragma是Z让编译器~译出的C或C++E序与机器硬件和操作pȝ保持完全兼容而定义的宏扩展,#pragma是和特定~译器相关的?/p>
一、Pragma说明(Directives)
C和C++E序的每ơ执行都支持其所在的L或操作系l所h的一些独特的特点?br>一些程?例如Q需要精控制数据存攄内存区域或控制某个函数接收的参数?br>#pragma指示为编译器提供了一U在不同机器和操作系l上~译以保持C和C++完全兼容的方法。Pragmas是由机器和相关的操作pȝ定义的,通常Ҏ个编译器来说是不同的?/p>
二、语?Syntax)
#pragma token-string(特征字符?特征字符串是一q串的字W,是要给一个特定编译器提供说明和编译意见?/p>
W号(#)必须是pragma所在那一行的W一个非I格字符;
#号和pragma之间可以有Q意个I格W?br>?pragma之后Q是可以被编译器解析的预处理特征字符?br>一般认?#pragma属于宏扩展?br>如果~译器发C认识的pragma,会提告,但l编译下厅R?/p>
Pragmas可以用在条g声明上,提供最新的功能性的预处理程序,或者提供给~译器定义执行的信息?/p>
其格式一般ؓ: #pragma para
其中para为参敎ͼ下面来看一些常用的参数?br>
alloc_text
comment //注释
init_seg1
optimize //最优化
auto_inline
component //l成部g
inline_depth
pack //?br>bss_seg
data_seg
inline_recursion //内嵌递归
pointers_to_members1
check_stack
function
intrinsic //内在?br>setlocale
code_seg
hdrstop
message
vtordisp1
const_seg
include_alias
once
warning
参数详解
diary files\#pragma 预处理指令详?doc
* 递归锁:另外一斚wQ递归锁允?/span>acquireҎ嵌套调用Q只要当前拥有该锁的U程是试图重新获取它的U程。递归锁对于回调驱动的事g分派构架Q比?/span>
#include <iostream.h>
#include <string.h>
……
string st("test string");
……
error C2065: 'string' : undeclared identifier
解释Q?/p>
#include <string>
using namespace std;
因ؓ? using namespace std; 情况下,
#include <string> 是? C++ string cdQ?nbsp;
#include <cstring> 是? C 的string 操作库函? ...
l节 加不?#8220;.h”
#include <iostream.h>
#include <string>
using namespace std;
没有错!Q?/p>
?/p>
#include <iostream.h>
#include <string.h>
using namespace std;
~译有错Q!
解释
“string.h“q个头文件是“旧式c头文?#8221;Q而这个文件中没有定义stringc(q点应该不奇怪,c语言中哪有什么类啊)Q这个头文g里面是有?#8220;旧式char-based字符?#8221;的操作函敎ͼ注意都是操作char*字符串的“函数”Q所以你引用q个头文Ӟ~译器肯定找不到“string”了?
“string”q个头文Ӟ没有扩展名)是C++标准化之后的C++头文Ӟ里面才有stringcȝ相关定义Q其实,stringq不是类Q是一个typedefQ但是用的时候不用去他Q,而C++标准头文件中的东襉K攑֜namespace std中了Q所以用的时候要“using namespace std”?
附:g不要?iostream.h"Q改?#8220;iostream”吧,因ؓ标准已经明确规定不在支持"iostream.h"q种头文件了?/p>
标准写法
#include <iostream>
#include <string>
using namespace std;
F: Z么using namespace std;
要写在include后面Q?/p>
Q: 因ؓinclude的文件包含名字域std
如果你把using namespace std写在前面Q编译器q不到stdq个名字
引用提高效率Q又不在函数中改变。参考:file:///F:/fragments/documents/深入探讨C++中的引用%20-%20KiRa的专?20-%20CSDNBlog.mht
常引?/p>
常引用声明方式:const cd标识W?&引用?= 目标变量名;
constcd ?& 是分不开的,引用一般定义成const?/p>
引用作ؓq回?/p>
要以引用q回函数|则函数定义时要按以下格式Q?/p>
cd标识W?&函数名(形参列表及类型说明)
{
函数?/p>
}
说明Q?/p>
Q?Q以引用q回函数|定义函数旉要在函数名前?amp;
Q?Q用引用q回一个函数值的最大好处是Q在内存中不产生被返回值的副本?/p>
例子Q?a href="http://www.shnenglu.com/robinson119/admin/C++exams/rtn_ref">Diary files\C++exams\rtn_ref
引用作ؓq回|必须遵守以下规则Q?/p>
Q?Q不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用成Z"无所?的引用,E序会进入未知状态?
Q?Q不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这U情况(q回函数内部new分配内存的引用)Q又面其它尬局面。例如,被函数返回的引用只是作ؓ一个时变量出玎ͼ而没有被赋予一个实际的变量Q那么这个引用所指向的空_由new分配Q就无法释放Q造成memory leak?/p>
Q?Q可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某U业务规则(business ruleQ相兌的时候,其赋值常怸某些其它属性或者对象的状态有养I因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针)Q那么对该属性的单纯赋值就会破坏业务规则的完整性?/p>
Q?Q引用与一些操作符的重载:
操作符<<?gt;>Q这两个操作W常常希望被q箋使用Q例如:cout << "hello" << endl; 因此q两个操作符的返回值应该是一个仍然支持这两个操作W的引用。可选的其它Ҏ包括Q返回一个流对象和返回一个流对象指针。但是对于返回一个流对象Q程序必重斎ͼ拯Q构造一个新的流对象Q也是_q箋的两?lt;<操作W实际上是针对不同对象的Q这无法让h接受。对于返回一个流指针则不能连l?lt;<操作W。因此,q回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许q就是C++语言中引入引用这个概늚原因吧?赋值操作符=。这个操作符象流操作W一P是可以连l用的Q例如:x = j = 10;或?x=10)=100;赋值操作符的返回值必L一个左|以便可以被l赋倹{因此引用成了这个操作符的惟一q回值选择?/p>
// 被调用函数是以int为参敎ͼ以int回?br>__stdcall int callee(int);
// 调用函数以函数指针ؓ参数
void caller( __cdecl int(*ptr)(int));
// 在p中企囑֭储被调用函数地址的非法操?br>__cdecl int(*p)(int) = callee; // 出错
指针p和callee()的类型不兼容Q因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针pQ尽两者有相同的返回值和参数列?nbsp;
先定义函敎ͼvoid print(string str)Q?/p>
声明一个函数指针,void ( *p )( string ) = NULL;
指向copy函数Qp = & print; ?p = printQ?/p>
调用指针函数Qp( “hello” );
可以把多个函数放在一个指针数l里Q然后通过数组对应指针方便调用函数?/p>
例子Q?a href="http://www.shnenglu.com/robinson119/admin/C++exams/pointer_func">Diary files\C++exams\pointer_func
动态绑定一个函?/p>
void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函?*/
}
void func();
int main()
{
p = func;
caller(p); /* 传递函数地址到调用?*/
}
如果赋了不同的值给pQ不同函数地址Q,那么调用者将调用不同地址的函数。赋值可以发生在q行Ӟq样使你能实现动态绑定?/p>
动态联~:E序q行之后才完成的联编Q系l在q行时动态实现某一动作?/span>
静态多态性:函数重蝲Q运符重蝲Q?/span>
动态多态性:l承Q虚函数Q?br>
函数重蝲
1 参数有差别?/span>
2 参数相同Q属不同cR?/span>
一个重载的例子Q?/span>Diary files\C++exams\func_reload
名字压gQ?/span>是编译器Ҏ函数重蝲的不同类型,在编译时改变函数名字Q而达到区分的目的?/span>
q要注意Q友元函数的入口包括多个c,那这些类都要在友元函C前声明?/p>
如例子:Diary files\C++exams\frnd_vst2_func
如果是类person的两个承他的子cboy和girlQ那么友元函数如何声明和定义?/p>
Diary files\C++exams\frnd_member
q需要注意的地方有:
基类的数据成员共有;构造函敎ͼ构造函数初始化表;cȝ声明先于友元函数Q提高了重用性;
基类的数据成员必d有,因ؓ构造函C讉K到它们,子类的构造函C会引用基cȝ构造函敎ͼ如果是私有的会是的子cL造函C能直接访问基cȝU有成员Q要通过基类的成员函数来讉K?/p>
子类Qpublic基类
q有写细节问题:
头文Ӟchar *a的声明和使用Q分配char*数据Q?/p>
例如Q?/p>
char *name;
name = new char[ strlen( s ) + 1 ];
友元函数的声明时的入口参数可以只写类型,不写参数名?/p>
例如Q?/p>
class boy
{
……
void disp( gril & );
}
注意声明时的入口参数可省略,但要?amp;?/p>
定义Ӟ写成
void disp( gril &x ){ …… }
int i; <class> *thisQ?/p>
this指针是一个隐含指针,是成员函数所属对象的指针?/p>
每个成员函数都有一个this指针Qthis指针指向该函数所属类的对象?/p>
使用格式Qthis -> 成员变量
不同的对象调用同一个成员函敎ͼC++会根据成员函数的this指针指向哪一个对象,来调用该对象的成员变量?/p>
this指针如何得到对象名?
例如Q一个类class me的初始化函数Q?/p>
void init(char ma, int mb)
{ a = ma; b = mb; }
~译Ӟ被编译器转化为:
void init(me *this, char ma, int mb) //多了一个this指针的参敎ͼ指向mecȝobj
{ this -> a = ma, this -> b = mb; }
对于只做输入用的指针参数Q最好用constQ这样避免指针参数被修改?
比如Q对于上面的StringCopy的例子,写成
void StringCopy (char *strDestination, const char *strSource); //比较安全?/p>
但是如果输入参数以g递的形式传递对象,则改Z用const &最好,因ؓ省去了时对象的构造和解析的过E,提高效率和安全性?/p>
int printfQ?#8230;…Q;q个函数的参C定Q没有严格的cd查,所以最好不用?/span>