函数重载和覆盖
重载关系的产生:
- 同一作用域内
- 函数名必须相同
- 形参表必须不同
重载关系的函数的调用
- 根据实参类型和形参的类型进行匹配,调用最匹配的函数
重载和隐藏
- 只有同一作用域内的同名函数才涉及重载的关系
- 不同作用域的同名函数涉及的是隐藏关系
// 详谈同一作用域
#include <iostream>
using namespace std;
namespace ns1{
void foo( char* c, short s){
cout << "1. foo(char*, short)" << endl;
}
void foo( int i, double d){
cout << "2. foo(int, double)" << endl;
}
}
namespace ns2{
void foo( const char* c, short s){
cout << "3. foo(const char*, s)" << endl;
}
void foo( double d, int i ){
cout << "4. foo(double, int)" << endl;
}
}
int main( void ){
// 名字空间指令,从这行代码开始,ns1中的内容在当前作用域可见(出现在可见表中)
using namespace ns1;
// 名字空间声明,从这行代码开始,ns2中的foo引入当前作用域(出现在定义表中)
using ns2::foo;
//名字同时出现在定义表和可见表时,先从定义表读取,忽略可见表
char* c; short s;
foo( c, s ); // 3
return 0;
}
重载解析
- 完全匹配>常量转换>升级转换>标准转换>自定义转换>省略号匹配
函数指针的类型决定其调用的哪个版本的重载函数
// 重载匹配的优先级
#include <iostream>
using namespace std;
void foo( char* c, short s){ // 完全匹配 _Z3fooPcs
cout << "1. foo(char*, short)" << endl;
}
void foo(const char* c, short s){ // 常量转换 _Z3fooPKcs
cout << "2. foo(const char*,short)" << endl;
}
void foo(char* c, int s){ // 升级转换 _Z3fooPci
cout << "3. foo(char*,int)" << endl;
}
void foo(char* c, char s){ // 标准转换 _Z3fooPcc
cout << "4. foo(char*,char)" << endl;
}
void foo( ... ){ // 省略号匹配 _Z3fooz
cout << "5. foo( ... )" << endl;
}
int main( void ){
char* c; short s;
// 普通方式调用重载关系的函数,根据实参类型和形参类型匹配,来确定调用哪一个foo 1
foo( c, s );
void (*pfunc)(const char*,short) = foo; // _Z3fooPKcs
// 函数指针方式调用重载关系的函数,根据函数指针本身的类型,来确定调用哪一个foo 2
pfunc(c,s);
return 0;
}
重载是通过C++换名机制来实现的
通过extern“C”可以要求C++编译器按照C方式编译函数,即不做换名,当然也就无法重载。
动态内存分配
new /delete会在堆中分配和释放内存
注意事项:
- 不能通过delete释放已经释放过的内存
- delete野指针的后果未定义,但是delete空指针是安全的
- new申请内存失败,会抛出异常
int * pi = new int;
delete pi;//野指针,对它再次delete的后果很严重
pi=NULL;//空指针,对它再次delete是安全的
分配内存的同时初始化
int * pi = new int(10);
数组
int* pi = new int[4]{10,20,30,40};
delete[] pi;
多维数组
- 通过new分配的N维数组,返回N-1维数组指针
int (*prow)[4] = new int[3][4]
int (*ppage)[4][5] = new int[3][4][5]
// 动态(堆)内存分配
#include <iostream>
#include <cstdlib>
using namespace std;
int main( void ){
// int* pm = (int*)malloc( 4 );
// cout << "*pm=" << *pm << endl; // 初始值为0
// free( pm ); // 当这行代码执行完毕后,pm指向的堆内存被释放,进而pm变为野指针
// pm = NULL;
// free( pm ); // 给free传递的为野指针,释放野指针后果很严重,释放空指针是安全的
//
// int* pn = new int(100);
// cout << "*pn=" << *pn << endl;
// delete pn; // 当这行代码执行完毕后,pn指向的堆内存被释放,进而pn变为野指针
// pn = NULL;
// delete pn; // 给delete传递的为野指针,释放野指针后果很严重,释放空指针是安全的
//
int* pi = new int[4]{10,20,30,40}; // 以数组的方式来new,返回的永远是第一个元素的地址
for( int i = 0; i < 4; i++)
cout << pi[i] << ' ';
cout << endl;
delete[] pi;
pi = NULL;
// 不管是几维数组,都应该当做一维数组看待
int(*p)[4] = new int[3][4]{{1,2,3,4},{11,22,33,33},{111,222,333,333}}; // 以数组的方式来new,返回的永远为第一个元素的地址
/*
int (*p)[4] = a;//声明定义一个 拥有3个一维数组(int[4])的数组指针
*p指向第一个元素的地址,即第一个一维数组的地址
*/
cout << (*p)[1] << endl; //2 (*p)[1] 代表访问第1个一维数组的第2个元素
cout << p[1][2] << endl; // 33 p[1][2] 代表访问第2个一维数组的第3个元素
cout << endl;
delete[] p;
// try{
// new int[0xFFFFFFFFFFFF];
// }
// catch(...){
// }
return 0;
}
引用
- 引用即内存的别名
- 引用本身不占内存,并非实体,对引用的所有操作都是在对目标内存进行操作
- 引用必须初始化,且不能更换目标
- 不存在引用的引用
- 引用的常属性必须和目标的常属性一致
- 引用可以限定更加严格
- 常引用可以引用右值:延长右值的生命周期
- 常引用 为 万能引用。
int a=10;
int& b=a;
int c = 20;
b = c;//仅仅是在对引用的目标内存进行赋值
#include <iostream>
using namespace std;
int main( void ){
int a = 10;
int& b = a; // 这并不是利用a的数据给b赋值,而应该理解为引用b是a所代表的内存的别名
int& d = b; // 这并不是引用的引用,而应该理解为d是b的目标内存的引用,也就是说,d是a的引用
cout << "&a:" << &a << ", &b:" << &b << ", &d:" << &d << endl;
const int e = 10;
// int& f = e; // error 别名不可以比真名限定的更加宽松
const int& g = e; // ok
const int& h = a; // ok, 别名可以比真名限定的更加严格
return 0;
}