仅作个人笔记学习交流使用
进击的C++(二)
一、函数参数
参数默认值
C++可以在函数声明时为参数提供一个默认值,当函数调用时没有提供参数的值,则使用默认值。
参数的默认值必须在函数声明中指定。
参数的默认值必须从右向左提供。
函数调用时使用了默认值,则后续参数必须使用默认值。
int square(int x = 0);
int square(int x)
{
return x*x;
}
square(); //square(0) --> 0
square(2); //square(2) --> 4
占位参数
占位参数只有参数类型声明,而没有参数名声明。
一般情况下,在函数体内部无法使用占位参数。
占位参数与默认参数结合使用,兼容C中可能的不规范写法(如 f(void) )。
void fun (int a,float)
{}
二、函数重载(overload)
重载——同一个标识符在不同的上下文中有不同的意义
函数重载——用同一个函数名定义不同的函数
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
函数重载至少满足以下一个条件:
- 参数个数不同
- 参数类型不同
- 参数顺序不同
int add(int a, int b) // int(int, int)
{
return a + b;
}
int add(int a, int b, int c) // int(int, int, int)
{
return a + b + c;
}
编译器函数重载的准则:
- 将所有同命函数作为候选者
- 尝试寻找可行的候选函数
- 精确匹配实参
- 通过默认参数能够匹配实参
- 通过默认类型转换匹配实参
- 匹配失败
- 候选函数不唯一,出现二义性,编译失败
- 无法匹配所有候选者,函数未定义,编译失败
注意
重载函数在本质上是相互独立的不同函数
重载函数的函数类型不同
函数重载时由函数名和参数列表决定的
函数的返回值不能作为函数重载的依据
函数重载必然发生在同一个作用域中
函数重载与函数指针
将重载函数名赋值给函数指针时:
- 根据重载规则选择与函数指针参数列表一致的候选者
- 严格匹配候选者的函数类型与函数指针的函数类型
- 无法直接通过函数名得到重载函数的入口地址
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC1)(int); // int func(int x)
typedef int(*PFUNC2)(int,int); // int func(int a,int b)
typedef int(*PFUNC3)(const char*); // int func(const char* s)
三、C++调用C
C++编译器优先使用C++编译方式,能够兼容C的编译方式,使用extern强制让C++使用C编译方式。
__cplusplus 是C++编译器内置的标准宏定义,确保C代码以统一的C方式被编译为目标文件。
extern "C"
{
//C code
}
#ifdef __cplusplus
extern "C" {
#endif
#include "Ccode.h"
//C code
#ifdef __cplusplus
}
#endif
注意
C++不能以C的方式编译重载函数
编译方式决定函数名被编译后的目标名:
- C++编译方式将函数名和参数列表编译成目标名
- C编译方式只将函数名编译成目标名
四、C++动态内存分配
C++中通过new进行动态内存申请
C++中的动态内存申请是基于类型进行的
C++通过delete进行内存释放
Type* pointer = new Type;
delete pointer;
Type* pointerarr = new Type[N];
delete[] pointerarr;
new 与 malloc 对比
new | malloc |
---|---|
new是C++的关键字 | malloc是C库提供的函数 |
new 以具体类型为单位进行内存分配 | malloc以字节为单位进行内存分配 |
new 在申请单个类型变量时可进行初始化 | malloc不具备内存初始化特性 |
new 初始化
int* pi = new int(1); //()初始化
// int* pa = new int[1]; //[]数组
float* pf = new float(2.0f);
char* pc = new char('c');
五、命名空间
C中只有一个全局作用域
- 所有全局标识符共享同一个作用域
- 标识符之间可能发生冲突
C++ 命名空间
- 命名空间将全局作用域划分成不同的部分
- 不同命名空间中的标识符可以同名而不冲突
- 命名空间可以嵌套
- 全局作用域也叫默认命名空间
int j = 0;
namespace External
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
using namespace name; //使用整个命名空间
using namespace External;
using name::variable; //使用命名空间中的变量
using External::i;
using External::Internal::P;
using ::variable //使用默认命名空间中的变量
using ::j;
六、类型转换
强制类型转换 xxx_cast (Expression)
static_cast
- 用于基本类型间的转换
- 不能用于基本类型指针间的转换
- 用于有继承关系类对象之间的转换
- 用于类指针之间的转换
void static_cast_demo()
{
int i = 0x12345;
char c = 'c';
c = static_cast<char>(i); //c='E'
int* pi = &i;
char* pc = &c;
pc = static_cast<char*>(pi); //error: static_cast from 'int *' to 'char *' is not allowed
}
const_cast
- 用于去除变量的只读属性
- 强制转换的目标类型必须是指针或引用
void const_cast_demo()
{
const int& j = 1;
int& k = const_cast<int&>(j);
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
const int x = 2;
int& y = const_cast<int&>(x);
int z = const_cast<int>(x); //error: const_cast to 'int', which is not a reference, pointer-to-object, or pointer-to-data-member
y = 8;
printf("x = %d\n", x); //x=2
printf("y = %d\n", y); //y=8
printf("&x = %p\n", &x);
printf("&y = %p\n", &y);
}
reinterpret_cast
- 用于指针类型间的转换
- 用于整数和指针类型间的转换
void reinterpret_cast_demo()
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
pc = reinterpret_cast<char*>(pi);
pi = reinterpret_cast<int*>(pc);
pi = reinterpret_cast<int*>(i);
c = reinterpret_cast<char>(i); //error: reinterpret_cast from 'int' to 'char' is not allowed
}
dynamic_cast
- 用于有继承关系的类指针间的转换
- 用于又交叉关系的类指针间的转换
- 具有类型检查功能
- 需要虚函数支持
void dynamic_cast_demo()
{
int i = 0;
int* pi = &i;
char* pc = dynamic_cast<char*>(pi); //error: cannot dynamic_cast 'pi' (of type 'int*') to type 'char*' (target is not pointer or reference to class)
}