C++对C语言的扩充
命名空间
命名空间是C++语言的新特性,它能够解决命名冲突问题。
例如,小明定义了一个函数swap(),C++标准程序库中也存在一个swap()函数。此时,为了区分调用的是哪个swap()函数,可以通过命名空间进行标识。
C++中的命名空间包括两种:标准命名空间、自定义命名空间,具体介绍如下。
标准命名空间
std是C++标准命名空间,由于C++标准库几乎都定义在std命名空间中,因此编写的所有C++程序都需要引入下列语句。
using namespace std;
自定义命名空间
使用namespace可以自定义命名空间,示例代码如下所示:
namespace lib
{void func(){}
}
上述代码中,使用namespace定义了一个名称为lib的命名空间,该命名空间内部定义了一个func()函数。
如果要使用命名空间中定义的元素,有下列三种方式。
(1)使用“命名空间::元素”方式指定命名空间中定义的元素。
例如,如果要在屏幕上输出数据,并且在末尾加上换行,就要使用标准命名空间std中的cout和endl,在使用cout和endl时,在cout和endl前面加上命名空间和“::”作用域标识符,示例代码如下所示:
std::cout<<"C++"<<std::endl;
(2)使用using语句引用命名空间元素。
例如,如果在屏幕上输出数据,可以使用using引用标准命名空间中的cout,这样在后面的代码中可以随意使用cout,示例代码如下所示:
using std::cout;
cout<<"C++";
需要注意的是,这种方式只能使用using引入的元素,例如,无法单独使用endl这个元素,但可以通过std::endl的形式使用endl。
(3)使用using语句直接引用命名空间。
using namespace std;
这样引入std空间后,std中定义的所有元素就都可以被使用了。但这种情况下,如果引用多个命名空间往往容易出错。例如,自定义swap()函数,标准库也有swap()函数,调用swap()函数就会出现二义性错误。针对这个问题,可以使用“命名空间::元素”方式指定具体要引用的元素。
匿名命名空间
命名空间还可以定义成匿名的,即创建命名空间时不写名字,由系统自动分配。例如,下面定义的命名空间就是匿名的。
namespace
{… //可以是变量、函数、类、其他命名空间
}
编译器在编译阶段会为匿名命名空间生成唯一的名字,这个名字是不可见的。除此之外,编译器还会为这个匿名命名空间生成一条using指令。编译后的匿名命名空间等效于下面的代码:
namespace _UNIQUE_NAME_
{… //可以是变量、函数、类、其他命名空间
}
using namespace _UNIQUE_NAME_;
匿名命名空间的作用是限制命名空间的内容仅能被当前源文件使用,其他源文件是无法访问的,使用extern声明访问也是无效的。
匿名命名空间(Anonymous Namespace)是一种特殊的命名空间,用于封装和隐藏不希望暴露给外部代码的实体。匿名命名空间中的实体只在当前源文件中可见,对其他源文件是不可见的。
在匿名命名空间中,可以声明和定义各种实体,如变量、函数、类等。这些实体只在当前源文件内可见,对于其他源文件而言,它们是隐藏的。
使用匿名命名空间的一个常见用途是在多个源文件中定义具有相同名称的静态变量。由于匿名命名空间中的实体在各源文件中是独立的,因此可以在每个源文件中定义同名的静态变量而不会发生冲突。
匿名命名空间还可以用于隐藏实现细节,防止这些细节被其他源文件访问到。这对于实现模块化和封装性的代码设计非常有用。
需要注意的是,匿名命名空间在不同源文件中是独立的,因此无法通过匿名命名空间访问其他源文件中的实体。如果需要在多个源文件中共享实体,可以使用具名命名空间来实现。
控制台输入/输出
C++控制台常用的输入/输出是由输入、输出流对象cin和cout实现的,它们定义在头文件iostream中,作用类似于C语言中的scanf()函数和printf()函数。下面分别对cin与cout的用法进行介绍。
cin
cin与运算符“>>”结合使用,用于读入用户输入,以空白(包括空格、Enter、Tab)为分隔符。
示例代码如下所示:
// 输入单个变量
char c1,c2;
cin>>c1;
cin>>c2;// 输入多个变量
string s;
float f;
cin>>s>>f;
输入单个变量时,如果从键盘输入字符’a’和’b’(以空格分隔),则cin语句会把输入的’a’赋值给变量c1,把输入的’b’赋值给变量c2。
输入多个变量时,如果输入字符串"abc"和数据3.14,则cin语句会把字符串"abc"赋值给变量s,把数据3.14赋值给变量f。
在默认情况下,运算符“>>”将跳过空白符,然后读入后面与变量类型相对应的值。因此,给一组变量输入值时可用空格符、回车符、制表符将输入的数据间隔开。
当输入字符串(即类型为string的变量)时,提取运算符“>>”的作用是跳过空白字符,读入后面的非空白字符,直到遇到另一个空白字符为止,并在串尾放一个字符串结束标志‘\0
’。
cout
cout与运算符“<<”结合使用,用于向控制台输出信息。cout可以将数据重定向输出到磁盘文件。具体用法如下。
(1)cout输出常量值。
cout<<10<<endl;
cout<<'a'<<endl;
cout<<"C++"<<endl;
(2)cout输出变量值。
//输出单个变量
int a =10;
cout<<a<<endl; //输出int类型的变量//输出多个变量
int a = 10;
char *str = "abc";
cout<<a<<","<<str<<endl;
在用cout输出变量值时,“<<”运算符会根据变量的数据类型自动匹配并正确输出。
(3)cout输出指定格式的数据。
使用cout输出指定格式的数据时,可以通过C++标准库提供的标志位和操作符控制格式,这些操作符位于iomanip头文件,使用时需要引入iomanip头文件。
下面介绍一些常见的输出格式控制。
①输出八进制、十进制、十六进制数据。
int a=10;
cout<<"oct:"<<oct<<a<<endl; // 以八进制输出a
cout<<"dec:"<<dec<<a<<endl; // 以十进制输出a
cout<<"hex:"<<hex<<a<<endl; // 以十六进制输出a
②输出指定精度数据。
使用setprecision()函数可以设置浮点类型输出所需精度数据,示例代码如下所示:
double f = 3.1415926;
cout<<"默认输出 :"<<f<<endl;
cout<<"精度控制"<<setprecision(7)<<setiosflags(ios::fixed)<<f<<endl;
③输出指定域宽、对齐方式、填充方式的数据。
C++提供了一些函数。
setw()函数用于指定域宽。
setiosflags()函数用于设置对齐方式。
setfill()函数用于设置填充方式。
下面通过案例演示如何输出指定域宽、对齐方式以及填充方式的数据,如例所示。
#include <iostream>
#include <iomanip>
using namespace std;int main()
{// 默认情况下,setw设置字段宽度为10,并且右对齐cout << setw(10) << 3.1415 << endl;// 使用setfill('0')设置填充字符为'0'cout << setw(10) << setfill('0') << 3.1415 << endl;// 使用setfill('0')设置填充字符为'0',同时使用setiosflags(ios::left)设置左对齐cout << setw(10) << setfill('0') << setiosflags(ios::left) << 3.1415 << endl;// 使用setfill('-')设置填充字符为'-',同时使用setiosflags(ios::right)设置右对齐cout << setw(10) << setfill('-') << setiosflags(ios::right) << 3.1415 << endl;return 0;
}
类型增强
C语言和C++语言都属于强类型语言,相比于C语言,C++中的类型检查更加严格,下面介绍C++对常见类型的增强。
1.常变量类型const
使用const修饰的变量称为常变量,C语言中的常变量可以通过指针修改,而C++中的常变量无法通过指针间接修改。
示例代码如下所示:
const int a = 10;
int* p = &a; // 类型不兼容错误
*p = 20; //无法通过指针修改常变量
2.逻辑类型bool
C语言中没有逻辑类型,只能用非0表示真,用0表示假。C++增加了bool类型,使用true表示真,false表示假。示例代码如下所示:
bool a = false; //定义bool类型变量
bool b = true;
bool greater(int x, int y){return x > y;} // 比较x是否大于y
3.枚举类型enum
C语言中枚举类型只能是整数类型,且枚举变量可以用任意整数赋值,使用自由灵活。在C++中,枚举变量只能使用枚举常量进行赋值。
下面代码在C++中是不被允许的。
enum temperature {WARM,COOL,HOT};
enum temperature t= WARM;
t=10; //错误
默认参数
“默认”的概念大家都不陌生,比如安装一款软件时,在安装过程中会有默认参数选项,如默认安装路径,安装时可以修改默认安装路径。
C++的函数支持默认参数,即在声明或者定义函数时指定参数的默认值。
下面通过案例演示默认参数的用法,如例所示。
#include <iostream>
using namespace std;
void add(int x, int y = 1, int z = 2)
{cout << x + y + z << endl;
}
int main()
{add(1); // 只传递1给形参x,y、z使用默认形参值add(1, 2); // 传递1给x,2给y,z使用默认形参值add(1, 2, 3); // 传递三个参数,不使用默认形参值return 0;
}
在函数原型中,所有取默认值的参数都必须出现在不取默认值的参数的右边。
示例:
int fun(int a, int b, int c = 111);
在函数调用时,若某个参数省略,则其后的参数皆应省略而采取默认值。不允许某个参数省略后,再给其后的参数指定参数值。
使用默认参数时,需要注意以下规则:
(1)默认参数只可在函数声明中出现一次,如果没有函数声明,只有函数定义,那么可以在函数定义中设定。
(2)默认参数赋值的顺序是自右向左,即如果一个参数设定了默认参数,则其右边不能存在未赋值的形参。
(3)默认参数调用时,遵循参数调用顺序,即有参数传入时它会先从左向右依次匹配。
(4)默认参数值可以是全局变量、全局常量,甚至可以是一个函数。
当进行函数调用时,编译器按从左到右的顺序将实参与形参结合,若未指定足够的实参,则编译器按顺序用函数原型中的默认值来补足所缺少的实参。
函数重载
在编程语言里存在一种情况,参数不同的函数有着相同的名字,调用时根据参数不同确定调用哪个函数,这就是C++的函数重载机制。
所谓函数重载(overload),就是在同一个作用域内函数名相同但参数个数或者参数类型不同的函数。
例如,在同一个作用域内同时定义三个add()函数,这三个add()函数就是重载函数,示例代码如下所示:
void add(int x, int y);
void add(float x);
double add(double x, double y);
下面通过案例演示函数重载的用法,如例所示。
#include <iostream>
using namespace std;
void add(int x, int y)
{cout << "int: " << x + y << endl;
}
void add(double x)
{cout << "double: " << 10 + x << endl;
}
double add(double x, double y)
{return x + y;
}
int main()
{add(10.2); // 一个double类型参数add(1, 3); // 两个int类型参数return 0;
}
调用重载函数时,编译器会根据传入的实参与重载函数逐一匹配,根据匹配结果决定到底调用哪个函数。
如果重载函数中的形参没有默认参数,定义和调用一般不会出现问题,但是当重载函数有默认参数时,需要注意调用二义性。例如,下面的两个add()函数:
int add(int x, int y = 1);
void add(int x);
当调用add()函数时,如果只传入一个参数就会产生歧义,编译器无法确认调用哪一个函数,这就产生了调用的二义性。在编写程序时,要杜绝重载的函数有默认参数,从而避免调用二义性的发生。
调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。
示例如下:
int mul(int x, int y);
double mul(int x, int y);
在调用函数时,如果给出的实参和形参类型不相符,C++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。
示例如下:
void f_a(int x);
void f_a(long x);
f_a(20.83);
引用
引用是C++引入的新语言特性,它是某一变量的一个别名,使用“&”符号标识。引用的定义格式如下:
数据类型& 引用名 = 变量名;
习惯使用C语言开发的读者看到“&”符号就会想到取地址。但是在C++引用中,“&”只是起到标识的作用。
#include <iostream>
using namespace std;
int main()
{int a = 10;int &ra = a;cout << "变量a的地址" << hex << &a << endl;cout << "引用ra的地址:" << hex << &ra << endl;cout << "引用ra的值:" << dec << ra << endl;return 0;
}
在定义引用时,有以下几点需要注意。
(1)引用在定义时必须初始化,且与变量类型保持一致。
(2)引用在初始化时不能绑定常量值,如int&b=10是错误的。
(3)引用在初始化后,其值不能再更改,即不能用作其他变量的引用。
在C++中,引用的一个重要作用是作为函数参数。下面通过案例演示引用作为函数参数的用法。
#include <iostream>
using namespace std;// 定义一个交换函数,使用引用传递参数
void exchange(int &x, int &y)
{int temp = x; // 临时变量存储 x 的值x = y; // 将 y 的值赋给 xy = temp; // 将临时变量的值赋给 y,完成交换
}int main()
{int a, b;cout << "please input two nums: " << endl;cin >> a >> b; // 输入两个整数exchange(a, b); // 调用交换函数cout << " exchange: " << a << " " << b << endl; // 输出交换后的结果return 0;
}
引用是隐式的指针,但引用却不等同于指针,使用引用与使用指针有着本质的区别。
(1)指针指向一个变量,需要占据额外的内存单元,而引用指向一个变量,不占据额外内存单元。
(2)作为函数参数时,指针的实参是变量的地址,而引用的实参是变量本身,但系统向引用传递的是变量的地址而不是变量的值。
显然,引用比指针更简单、直观、方便。使用引用可以代替指针的部分操作。在C语言中只能用指针来处理的问题,在C++中可以通过引用完成,从而降低了程序设计的难度。
如果想使用常量值初始化引用,则引用必须用const修饰,用const修饰的引用称为const引用,也称为常引用。
const引用可以用const对象和常量值进行初始化。示例代码如下所示:
const int &a = 10;
const int b = 10;
const int &rb = b;
常变量的引用必须是const引用,但const引用不是必须使用常量或常变量进行初始化,const引用可以使用普通变量进行初始化,只是使用普通变量初始化const引用时,不允许通过该引用修改变量的值。
当引用作函数参数时,也可以使用const修饰,表示不能在函数内部修改参数的值。例如下面的函数,比较两个字符串长度:
bool isLonger(const string &s1, const string &s2)
{ return s1.size() > s2.size();
}
在isLonger()函数中,只能比较两个字符串长度而不能改变字符串内容。
字符串类(string)
C语言不存在字符串类型,都是用字符数组处理字符串,C++支持C风格的字符串,另外还提供了一种字符串数据类型:string。string是定义在头文件string中的类,使用前需要包含头文件string。
使用string定义字符串比较简单,主要有以下几种方式:
string s1;
s1="hello C++"; //第一种方式
string s2="hello C++"; //第二种方式
string s3("hello C++"); //第三种方式
string s4(6,'a'); //第四种方式
第一种方式先定义了字符串变量s1,再为字符串变量s1赋值;
第二种方式直接使用“=”为字符串变量s2赋值;
第三种方式在定义字符串变量时,将初始值放在“()”运算符中,使用“()”运算符中的字符串为变量初始化;
第四种方式在定义字符串变量时,也将初始值放在“()”运算符中,但是“()”中有两个参数,第一个参数表示字符个数,第二个参数表示构成字符串的字符。上述代码最后一行,表示用6个字符’a’构成的字符串初始化变量s4,初始化后s4的值为"aaaaaa"。
注意:
使用string定义字符串时,不需要担心字符串长度、内存不足等情况,而且string类重载的运算符与成员函数足以完成字符串的各种处理操作。
下面介绍一些常见的string字符串操作。
1.访问字符串中的字符
string类重载了“[]
”运算符,可以通过索引方式访问和操作字符串中指定位置的字符。示例代码如下所示:
string s="hello,C++";
s[7]='P';
s[8]='P';
上述代码中,通过索引将字符串s中的两个“+”都修改成了’P’。
2.字符串的连接
在C语言中,连接两个字符串要调用strcat()函数,还要考虑内存溢出情况。在C++中,string重载了“+”运算符,可以使用“+”运算符连接两个string类型的字符串,示例代码如下所示:
string s1,s2;
s1="我在学习";
s2="C++";
cout<<s1+s2<<endl; //我在学习C++
3.字符串的比较
在C语言中,比较两个字符串是否相等需要调用strcm p()函数,而在C++中,可以直接调用重载的“>”“<”“ == ”等运算符比较两个string字符串。示例代码如下所示:
string s1,s2;
cin>>s1>>s2;
//比较两个字符串内容是否相同
if(s1>s2) cout<<"字符串s1大于s2"<<endl;
else if (s1<s2) cout<<"字符串s2大于s1"<<endl;
else cout<<"字符串s1与s2相等"<<endl;
上述代码通过“>”“<”“ == ”运算符比较用户输入的两个字符串的内容是否相同。
4.字符串的长度计算
string类提供的length()函数用于获取字符串长度。length()函数类似于C语言中的strlen()函数。调用length()函数获取字符串长度的示例代码如下所示:
string s="hello C++";
cout<<"length():"<<s.length()<<endl;
需要注意的是,由于计算结果不包括字符串末尾结束标志符“\0”,因此,上述代码使用length()函数计算出字符串s的长度为9。
5.字符串交换
string类提供了成员函数swap(),用于交换两个字符串的值,示例代码如下所示:
string s1="hello C++";
string s2="I Love China!";
s1.swap(s2); //通过“.”运算符方式交换
swap(s1,s2); //通过函数调用方式交换
需要注意的是,string的成员函数swap()只能交换string类型的字符串,不能交换C语言风格的字符串。
new/delete
C++增加了new运算符分配堆内存,delete运算符释放堆内存。具体用法如下。
1.使用new运算符分配堆内存
new运算符用于申请一块连续的内存,格式如下:
new 数据类型(初始化列表);
上述格式中,数据类型表示申请的内存空间要存储数据的类型;初始化列表指的是要存储的数据。如果暂时不存储数据,初始化列表可以为空,或者数据类型后面没有()。
如果内存申请成功,则new返回一个具体类型的指针;如果内存申请失败,则new返回NULL。
new申请内存空间的过程,通常称为new一个对象。与malloc()相比,new创建动态对象时不必为对象命名,直接指定数据类型即可,并且new能够根据初始化列表中的值进行初始化。
下面介绍new运算符常见的几种用法。
(1)创建基本数据类型对象。
使用new创建基本数据类型对象,示例代码如下所示:
char* pc = new char; //存储char类型的数据
int* pi = new int(10); //存储int类型的数据
double* pd = new double(); //存储double类型的数据
上述代码分别用new创建了char、int、double三个对象。
char对象没有初始化列表,新分配内存中没有初始值;
int对象初始化列表为10,即分配一块内存空间,并把10存入该空间;
double对象初始化列表为空,编译器会用0初始化该对象。
(2)创建数组类型对象。
使用new创建数组对象,格式如下所示:
new 数据类型[数组长度];
使用new创建数组的示例代码如下所示:
char* pc = new char[10];
在上述代码中,指针pc指向大小为10的char类型数组。
2.使用delete运算符释放堆内存
用new运算符分配的内存在使用后要及时释放以免造成内存泄漏,C++提供了delete运算符释放new出来的内存空间,格式如下:
delete 指针名;
由上述格式可知,delete运算符直接作用于指针就可以释放指针所指向的内存空间。但是使用delete运算符释放数组对象时要在指针名前加上[]
,格式如下:
delete[]指针名;
如果漏掉了[],编译器在编译时无法发现错误,导致内存泄漏。
下面通过案例来演示new和delete的用法,如例所示。
#include <iostream>
using namespace std;
int main()
{int *pi = new int(10); // 创建一个int对象,初始值为10cout << "*pi=" << *pi << endl;*pi = 20; // 通过指针改变内存中的值cout << "*pi = " << *pi << endl;// 创建一个大小为10的char类型的数组char *pc = new char[10];for (int i = 0; i < 10; i++)pc[i] = i + 65; // 向数组中存入元素for (int i = 0; i < 10; i++)cout << pc[i] << " ";cout << endl;delete pi; // 释放int对象delete[] pc; // 释放char数组对象return 0;
}
extern"C"
在C++程序中,可以使用extern"C"标注C语言代码,编译器会将extern"C"标注的代码以C语言的方式编译。使用extern"C"标注C语言代码的格式具体如下:
extern"C"
{ // C语言代码
}
强制类型转换
与C语言的类型转换相比,C++的类型转换机制更加安全。C++提供了四个类型转换运算符应对不同类型数据之间的转换,下面分别进行介绍。
1.static_cast< type >(expression)
static_cast<>是最常用的类型转换运算符,主要执行非多态的转换,用于代替C语言中通常的转换操作。static_cast<>可以实现下列转换。
- 基本数据类型之间的转换。
- 将任何类型转换为void类型。
- 把空指针转换成目标类型的指针。
- 用于类层次结构中基类和派生类之间指针或引用的转换。向上转换(派生类转换为基类)是安全的;向下转换(基类转换为派生类)没有动态类型检查,是不安全的。
使用static_cast<>运算符进行类型转换的示例代码如下所示:
int a = 1;
float b = 3.14;
a = static_cast<int>(b); // 将float类型转换为int类型
b = static_cast<float>(a); // 将int类型转换为float类型
int *q = NULL;
void *p = NULL;
q = p; // 将空指针转换为int类型,C语言允许,C++不允许
p = q;
q = static_cast<int *>(p); // 将空指针转换为int类型指针
2.reinterpret_cast< type >(expression)
reinterpret_cast通常为操作数的位模式提供较低层的重新解释。
例如,如果将一个int类型的数据a转换为double类型的数据b,仅仅是将a的比特位复制给b,不作数据转换,也不进行类型检查。
reinterpret_cast要转换的类型必须是指针类型、引用或算术类型。
使用reinterpret_cast<>运算符进行类型转换的示例代码具体如下:
char c = 'a';
int d = reinterpret_cast<int &>(c);
int *p = NULL;
float *q = NULL;
p = q; // C 语言允许,C++语言不允许
q = p; // C 语言允许,C++语言不允许
p = static_cast<int *>(q); // static_cast无法转换
q = static_cast<int *>(p); // static_cast无法转换
p = reinterpret_cast<int *>(q);
q = reinterpret_cast<float *>(p)
3.const_cast< type >(expression)
const_cast<>用于移除const对象的引用或指针具有的常量性质,可以去除const对引用和指针的限定。
示例代码如下所示:
int num = 100;
const int *p1 = #
// 将常量指针转换为普通类型指针,去除const属性
int *p2 = const_cast<int *>(p1);
*p2 = 200;
int a = 100;
const int &ra = a;
// 将常量引用转换为普通类型引用,去除const属性
const_cast<int &>(ra) = 200;
需要注意的是,const_cast<>只能用于转换指针或引用。
4.dynamic_cast< type >(expression)
dynam ic_cast<>用于运行时检查类型转换是否安全,可以在程序运行期间确定数据类型,如类的指针、类的引用和void*。
dynam ic_cast<>主要应用于类层次间的向上转换和向下转换,以及类之间的交叉转换。在类层次间进行向上转换时,它和static_cast作用一致。不过,与static_cast相比,dynam ic_cast能够在运行时检查类型转换是否安全。
当向下转换时,如果基类指针或引用指向派生类对象,dynam ic_cast运算符会返回转换后类型的指针,这样的转换是安全的。如果基类指针或引用没有指向派生类对象,则转换是不安全的,转换失败时就返回NULL。
Bjarne Stroustrup对编写C++程序的建议
1.C++中几乎不需要用宏。
2.用const或enum定义显式的常量。
3.用inline避免函数调用的额外开销。
4.用模板定义函数或类型。
5.用namespace避免命名冲突。
6.变量在使用时声明并初始化,不要提前声明变量。
7.使用new和delete会比函数malloc()和free()更好,realloc()函数可以用vector()代替。
8.避免使用void*、指针算术、联合和强制转换。
9.尽量少用数组和C风格的字符串,标准库中的string和vector可以简化程序。
10.试着将程序考虑为一组由类和对象表示的相互作用的概念。
内联函数
在函数名前冠以关键字inline,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。
说明:
- 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
- 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
示例:
#include <iostream>
using namespace std;inline double circle(double r) //内联函数
{double PI = 3.14;return PI * r * r;
}int main()
{for (int i = 1; i <= 3; i++)cout << "r = " << i << " area = " << circle(i) << endl;return 0;
}
使用内联函数替代宏定义,能消除宏定义的不安全性
void型指针
void通常表示无值,但将void作为指针的类型时,它却表示不确定的类型。这种void型指针是一种通用型指针,也就是说任何类型的指针值都可以赋给void类型的指针变量。
需要指出的是,这里说void型指针是通用指针,是指它可以接受任何类型的指针的赋值,但对已获值的void型指针,对它进行再处理,如输出或者传递指针值时,则必须再进行显式类型转换,否则会出错。
void *pc;
int i = 123;
char c = 'a';
pc = &i;
cout << pc << endl; // 输出指针地址006FF730
cout << *(int *)pc << endl; // 输出值123
pc = &c;
cout << *(char *)pc << endl; // 输出值a