布尔类型是表示布尔量的数据类型,其值只有真(true)和假(false)两个值,布尔类型的数据占1个字节的存储空间。
实现此案例需要按照如下步骤进行。
步骤一:布尔类型
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { bool b; b = true; b = false; std::cout<<std::boolalpha<< b <<std::endl; std::cout<<sizeof(bool) <<std::endl; b = 5; b = 5.5; b = 'a'; std::cout<<std::boolalpha<< b <<std::endl; return0; }
上述代码中,以下代码:
bool b; b = true; b = false;
定义了布尔类型变量b,并将其赋值为true和false。
上述代码中,以下代码:
std::cout<<sizeof(bool) <<std::endl;
从运行结果可以看出,布尔类型占1个字节的存储空间。
上述代码中,以下代码:
b = 5; b = 5.5; b = 'a';
表示任何基本数据类型的数据均可直接复制给布尔变量b。
本案例中的完整代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { bool b; b = true; b = false; std::cout<<std::boolalpha<< b <<std::endl; std::cout<<sizeof(bool) <<std::endl; b = 5; b = 5.5; b = 'a'; std::cout<<std::boolalpha<< b <<std::endl; return0; }
同一作用域内,一组具有相同函数名,不同参数列表的函数,构成重载关系,这组名称相同的函数成为重载函数。重载函数通常完成的功能相近,这样做的好处是减少了函数名的数量,提供了程序的可读性。
实现此案例需要按照如下步骤进行。
步骤一:重载关系
代码如下:
#include <iostream> void print(void) { std::cout<<"call print function"<<std::endl; } void print(int n) { std::cout<<"n = "<< n <<std::endl; } void print(double d) { std::cout<<"d = "<< d <<std::endl; } void print(int* p) { std::cout<<"*p = "<< *p <<std::endl; } int main(int argc, constchar * argv[]) { print(); print(10); print(10.32); int n = 100; print(&n); return0; }
上述代码中,以下代码:
void print(void) { std::cout<<"call print function"<<std::endl; } void print(int n) { std::cout<<"n = "<< n <<std::endl; } void print(double d) { std::cout<<"d = "<< d <<std::endl; } void print(int* p) { std::cout<<"*p = "<< *p <<std::endl; }
定义了一组函数名为print的函数,但这些函数的参数均不相同,这就使它们构成了重载关系,相互称为重载函数。虽然只有一个函数名,但却是四个不同的函数。
注意:参数相同仅返回值不同的两个函数不构成重载关系。
代码如下所示:
void print(void) { std::cout<<"call print function"<<std::endl; } int print(void) { std::cout<<"call print function"<<std::endl; return1; }
上述代码将报错,print函数重定义。
步骤二:同一作用域内的函数才构成重载关系
代码如下:
void foo (void) { } namespace ns1 { void foo (int a) { } namespace ns2 { void foo (int a, int b) { } void bar (void) { foo (10, 20); } } void hum (void) { foo (30); } } void fun (void) { foo (); }
上述代码中有三个同名的foo函数,它们的参数也不相同,但他们不构成重载关系,因为他们分属三个不同的名字空间,在不同的作用域中。
本案例的完整代码如下所示:
#include <iostream> void print(void) { std::cout<<"call print function"<<std::endl; } void print(int n) { std::cout<<"n = "<< n <<std::endl; } void print(double d) { std::cout<<"d = "<< d <<std::endl; } void print(int* p) { std::cout<<"*p = "<< *p <<std::endl; } void foo (void) { } namespace ns1 { void foo (int a) { } namespace ns2 { void foo (int a, int b) { } void bar (void) { foo (10, 20); } } void hum (void) { foo (30); } } void fun (void) { foo (); } int main(int argc, constchar * argv[]) { print(); print(10); print(10.32); int n = 100; print(&n); return0; }
重载是通过C++换名实现的,换名机制限制了C和C++模块之间的交叉引用。为了能够让C++编译器按照C方式处理函数接口,可以使用extern关键字实现。
实现此案例需要按照如下步骤进行。
步骤一:C形式的调用规格
代码如下所示:
extern"C" { void printChar(char c) { std::cout<<"c = "<< c <<std::endl; } void printInt(int n) { std::cout<<"n = "<< n <<std::endl; } } int main(int argc, constchar * argv[]) { printChar('a'); printInt(10); return0; }
上述代码中,以下代码:
extern"C" { void printChar(char c) { std::cout<<"c = "<< c <<std::endl; } void printInt(int n) { std::cout<<"n = "<< n <<std::endl; } }
表示要求C++编译器按照C方式处理函数接口,即不做换名。由于无法执行换名操作,当然也就无法重载,所以需要用不同的函数名区分相似功能函数。
换名操作实际上就是在编译时,将匹配好的重载函数重新命名的方法。实际上,在编译后重载函数的函数名还是不相同的。
本案例的完整代码如下所示:
extern"C" { void printChar(char c) { std::cout<<"c = "<< c <<std::endl; } void printInt(int n) { std::cout<<"n = "<< n <<std::endl; } } int main(int argc, constchar * argv[]) { printChar('a'); printInt(10); return0; }
函数的缺省参数是指在定义函数的时候,给函数形参一个缺省值,当调用该函数的时后,若未给出实参,则与该实参相对应的形参取缺省值。函数的缺省参数是在编译阶段解决的,因此只能用常量、常量表达式或者全局变量等非局部化数值作为缺省值。
实现此案例需要按照如下步骤进行。
步骤一:函数的缺省参数
代码如下所示:
#include <iostream> void foo(int a, int b = 456) { std::cout<<"a = "<< a <<std::endl; std::cout<<"b = "<< b <<std::endl; } int main(int argc, constchar * argv[]) { foo(100, 200); foo(300); return0; }
上述代码中,以下代码:
void foo(int a, int b = 456) { std::cout<<"a = "<< a <<std::endl; std::cout<<"b = "<< b <<std::endl; }
定义了一个函数foo,该函数有两个形参,第二个形参的后面有一个数值456,它就是缺省参数。
注意:
缺省参数只能是常量、常量表达式或者全局变量,如下代码是错误的:
void foo(int a,int b = a);
带缺省参数的形参b被定义成局部变量形参a的值了。
上述代码中,以下代码:
foo(100, 200);
在调用函数foo时,对于带缺省参数的形参位置可以像以前一样定义实参,这样,缺省参数将变得无效,实参200被赋值给形参。
上述代码中,以下代码:
foo(300);
在调用函数foo时,对于带缺省参数的形参位置也可以不写任何实参值,这样,缺省参数将起作用,相当于把实参456被赋值给形参。
本案例的完整代码如下所示:
#include <iostream> void foo(int a, int b = 456) { std::cout<<"a = "<< a <<std::endl; std::cout<<"b = "<< b <<std::endl; } int main(int argc, constchar * argv[]) { foo(100, 200); foo(300); return0; }
new和delete是C++提供的两个运算符,分别用于动态内存的分配和释放。new相当于C语言中的malloc函数,delete相当于C语言中的free函数。
实现此案例需要按照如下步骤进行。
步骤一:动态内存分配
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; return0; }
上述代码中,以下代码:
int *p = newint;
为整型指针变量p分配4个字节的存储空间。
上述代码中,以下代码:
delete p;
将指针p所指向的存储空间释放掉。
上述代码中,以下代码:
p = newint(100);
为整型指针变量p分配4个字节的存储空间,同时将新分配的存储空间初始化为整数100。
步骤二:不能与C语言中的动态内存分配函数混用
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; return0; }
上述代码中,以下代码:
p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p;
是不建议使用的方法,它会带来不可预知的问题。
步骤三:数组的分配与释放
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; p = newint [4] {10, 20, 30, 40}; for (int i = 0; i <4; i++) std::cout<< p[i] <<' '; std::cout<<std::endl; delete[] p; int (*row) [4] = newint [3][4]; row[1][2] = 15; delete[] row; return0; }
上述代码中,以下代码:
p = newint [4] {10, 20, 30, 40};
为整型指针变量p分配4个整型变量所占的存储空间,即4*4 = 16个字节。这样相当于分配了一个整型数组。用new操作符动态分配数组时,会在数组首元素前面多分配4或8个字节,用以存放数组的长度。
上述代码中,以下代码:
delete[] p;
将分配的16个字节的存储空间释放。注意此时一定不要忘记中括号[]。
上述代码中,以下代码:
int (*row) [4] = newint [3][4];
可以分配多维数组,但需要注意的是,高维数组释放时,依然使用如下语句:
delete[] row;
步骤四:野指针和空指针
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; p = newint [4] {10, 20, 30, 40}; for (int i = 0; i <4; i++) std::cout<< p[i] <<' '; std::cout<<std::endl; delete[] p; int (*row) [4] = newint [3][4]; row[1][2] = 15; delete[] row; int *p1 = newint; *p1 = 200; delete p1; delete p1; int *p2; delete p2; int *p3 = NULL; delete p3; return0; }
上述代码中,以下代码:
int *p1 = newint; *p1 = 200; delete p1; delete p1;
的最后一行,重复释放p1。因为倒数第二行释放后,p1已经变成野指针,释放野指针会导致程序崩溃。
上述代码中,以下代码:
int *p2; delete p2;
指针p2定义时没有初始化,p2也是野指针。
上述代码中,以下代码:
int *p3 = NULL; delete p3;
不会崩溃,因为delete运算符对空指针不作任何操作。
步骤五:内存分配失败
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; p = newint [4] {10, 20, 30, 40}; for (int i = 0; i <4; i++) std::cout<< p[i] <<' '; std::cout<<std::endl; delete[] p; int (*row) [4] = newint [3][4]; row[1][2] = 15; delete[] row; int *p1 = newint; *p1 = 200; delete p1; //delete p1; int *p2; //delete p2; int *p3 = NULL; delete p3; try { char* p = newchar[0xFFFFFFFF]; p[0] = 'a'; delete p; } catch (std::bad_alloc& ex) { std::cerr<<"内存分配失败!"<< std::endl; exit (-1); } return0; }
上述代码中,以下代码:
try { char* p = newchar[0xFFFFFFFF]; } catch (std::bad_alloc& ex) { std::cerr<<"内存分配失败!"<< std::endl; exit (-1); }
当使用new分配内存失败时,将抛出异常。
步骤六:定位分配
代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; p = newint [4] {10, 20, 30, 40}; for (int i = 0; i <4; i++) std::cout<< p[i] <<' '; std::cout<<std::endl; delete[] p; int (*row) [4] = newint [3][4]; row[1][2] = 15; delete[] row; int *p1 = newint; *p1 = 200; delete p1; //delete p1; int *p2; //delete p2; int *p3 = NULL; delete p3; try { char* p = newchar[0xFFFFFFFF]; p[0] = 'a'; delete p; } catch (std::bad_alloc& ex) { std::cerr<<"内存分配失败!"<< std::endl; exit (-1); } char* pool = newchar[1024]; // 分配内存池 int* pn = new (pool) int (123); char* ps = new (pool + 4) char[15]; strcpy (ps, "Hello, World !"); double* pd = new (pool + 19) double (3.14); std::cout<< *pn <<std::endl; // 123 std::cout<< ps <<std::endl; // Hello, World ! std::cout<< *pd<<std::endl; // 3.14 delete[] pool; // 释放内存池 return0; }
上述代码中,以下代码:
char* pool = newchar[1024]; // 分配内存池
分配了一个由1024个元素的字符数组,作为内存池使用。
上述代码中,以下代码:
int* pn = new (pool) int (123);
在内存池中分配4个字节作为一个整型变量使用。
上述代码中,以下代码:
char* ps = new (pool + 4) char[15];
在内存池中分配15个字节作为一个字符型数组使用。值得注意的是pool + 4是从内存池的第5个字节开始分配,否则上面分配的整型变量将被覆盖。
上述代码中,以下代码:
delete[] pool; // 释放内存池
在内存池中分配的变量或数组无需释放,只要最后一次性释放整个内存池即可。
本案例的完整代码如下所示:
#include <iostream> int main(int argc, constchar * argv[]) { int *p = newint; *p = 10; std::cout<< *p <<std::endl; delete p; p = newint(100); std::cout<< *p <<std::endl; delete p; p = newint; free(p); p = (int*)malloc(sizeof(int)); delete p; p = newint [4] {10, 20, 30, 40}; for (int i = 0; i <4; i++) std::cout<< p[i] <<' '; std::cout<<std::endl; delete[] p; int (*row) [4] = newint [3][4]; row[1][2] = 15; delete[] row; int *p1 = newint; *p1 = 200; delete p1; //delete p1; int *p2; //delete p2; int *p3 = NULL; delete p3; try { char* p = newchar[0xFFFFFFFF]; p[0] = 'a'; delete p; } catch (std::bad_alloc& ex) { std::cerr<<"内存分配失败!"<< std::endl; exit (-1); } char* pool = newchar[1024]; // 分配内存池 int* pn = new (pool) int (123); char* ps = new (pool + 4) char[15]; strcpy (ps, "Hello, World !"); double* pd = new (pool + 19) double (3.14); std::cout<< *pn <<std::endl; // 123 std::cout<< ps <<std::endl; // Hello, World ! std::cout<< *pd<<std::endl; // 3.14 delete[] pool; // 释放内存池 return0; }