一 布尔类型
1 bool类型是C++中的基本数据类型,专门表示逻辑值,逻辑真用true表示,逻辑假false表示
2 bool类型在内存占一个字节:1表示true,0表示false
3 bool类型变量可以接收任意类型表达式结果,其值非0则为true,为0则为假
#include
using namespace std;
int main(void){
bool b = false;
cout << "b=" << b << endl;//0
cout << "size=" << sizeof(b) << endl;//1
b = 3 + 5;
cout << "b=" << b << endl;//1
b = 1.2 * 3.4;
cout << "b=" << b << endl;//1
int* p = NULL;//NULL->(void*)0
b = p;
cout << "b=" << b << endl;//0
return 0;
}
八 C++的函数
1 函数重载(overload)
1)定义
在相同作用域,可以定义同名的函数,但是参数必须有所区分,这样函数构成重载关系.
注:函数重载和返回类型无关。
eg:实现图形库中一些绘图函数
//C语言
void drawRect(int x,int y,int w,int h){}
void drawCircle(int x,int y,int r){}
//C++语言
void draw(int x,int y,int w,int h){}
void draw(int x,int y,int r){}
#include
using namespace std;
int func(int i){
cout << "func(int)" << endl;
}
void func(int a,int b){
cout << "func(int,int)" << endl;
}
void func(int i,float f){
cout << "func(int,float)" << endl;
}
int main(void){
func(10);
func(10,20);
//func(10,3.14);//歧义错误,3.14默认当做double
func(10,3.14f);//func(int,float)
//由函数指针类型决定匹配的重载版本
void (*pfunc)(int,float) = func;
pfunc(10,20);
return 0;
}
2)函数重载匹配
调用重载关系的函数时,编译器将根据实参和形参的匹配程度,自动选择最优的重载版本,当前g++编译器匹配一般规则:
完全匹配>=常量转换>升级转换>降级转换>省略号
#include
using namespace std;
//char->int:升级转换
void bar(int i){
cout << "bar(1)" << endl;
}
//char->const char:常量转换
void bar(const char c){
cout << "bar(2)" << endl;
}
//short->char:降级转换
void hum(char c){
cout << "hum(1)" << endl;
}
//short->int:升级转换
void hum(int i){
cout << "hum(2)" << endl;
}
//省略号匹配
void fun(int i,...){
cout << "fun(1)" << endl;
}
//double->int:降级转换
void fun(int i,int j){
cout << "fun(2)" << endl;
}
int main(void){
char c = 'a';
bar(c);
short s = 100;
hum(s);
fun(100,3.14);
return 0;
}
3)函数重载原理
C++的编译器在编译函数时,会进行换名,将参数表的类型信息整合到新的函数名中,因为重载关系的函数参数表有所区分,换出的新的函数名也一定有所区分,解决了函数重载和名字冲突的矛盾。
笔试题:C++中extern "C"作用?
在C++函数声明时加extern "C",要求C++编译器不对该函数进行换名,便于C程序直接调用该函数.
注:extern "C"的函数无法重载。
2 函数的缺省参数(默认实参)
1)可以为函数参数指定缺省值,调用该函数时,如果不给实参,就取缺省值作为默认实参。
void func(int i,int j=0/缺省参数/){}
2)靠右原则:如果函数的某个参数带有缺省值,那么该参数右侧的所有参数都必须带有缺省值。
3)如果函数的声明和定义分开写,缺省参数应该写在函数的声明部分,而定义部分不写
#include
using namespace std;
//函数声明
void func(int a,int b = 20,int c = 30);
//void func(int i){}//注意歧义错误
int main(void){
func(11,22,33);
func(11,22);//11 22 30
func(11);//11 20 30
return 0;
}
//函数定义
void func(int a,int b/*=20*/,int c/*=30*/){
cout << "a=" << a << ",b=" << b << ",c="
<< c << endl;
}
3 函数的哑元参数
1)定义函数时,只有类型而没有变量名的形参被称为哑元
void func(int){…}
2)需要使用哑元场景
–》在操作符重载函数中,区分前后++、-- //后面讲
–》兼容旧的代码
算法库:void math_func(int a,int b){...}
使用者:
int main(void){
...
math_func(10,20);
...
math_func(10,20);
...
}
-----------------------------------------
升级算法库:void math_func(int a,int/*哑元*/){...}
使用者:
int main(void){
...
math_func(10,20);
...
math_func(10,20);
...
}
4 内联函数(inline)
1)使用inilne关键字修饰的函数,即为内联函数,编译器将会尝试进行内联优化,可以避免函数调用开销,提高代码执行效率.
inline void func(void){…}
2)使用说明
–》多次调用小而简单的函数适合内联优化
–》调用次数极少或大而复杂的函数不适合内联
–》递归函数不能内联优化
–》虚函数不能内联优化//后面讲
注:内联只是一种建议而不是强制的语法要求,一个函数能否内联优化主要取决于编译器,有些函数不加inline修饰也会默认处理为内联优化,有些函数即便加了inline修饰也会被编译器忽略。
九 C++动态内存管理
1 回顾C语言动态内存管理
1)分配:malloc()
2)释放:free()
2 C++动态内存管理
1)分配:new/new[]
2)释放:delete/delete[]
#include
using namespace std;
int main(void){
//动态分配内存保存1个int数据
//int* pi = (int*)malloc(4);//C
int* pi = new int;//C++
*pi = 123;
cout << *pi << endl;
//free(pi);
delete pi;//防止内存泄露
pi = NULL;//避免野指针
//动态分配内存同时初始化
int* pi2 = new int(200);
cout << *pi2 << endl;//200
(*pi2)++;
cout << *pi2 << endl;//201
delete pi2;
pi2 = NULL;
//动态分配内存保存10个int
//int* parr = new int[10];
//new数组同时初始化,需要C++11支持
int* parr =
new int[10]{1,2,3,4,5,6,7,8,9,10};
for(int i=0;i<10;i++){
//*(parr+i) = i+1
//parr[i] = i+1;
cout << parr[i] << ' ';
}
cout << endl;
delete[] parr;
parr = NULL;
return 0;
}
/
#include
using namespace std;
int main(void){
int* p1;
//delete p1;//delete野指针,危险!
int* p2 = NULL;
delete p2;//delete空指针,安全,但是无意义
int* p3 = new int;
delete p3;
delete p3;//不能多次delete同一个地址
return 0;
}
十 C++引用(Reference)
1 定义
1)引用即别名,引用就是某个变量别名,对引用操作和对变量本身完全相同.
2)语法
类型 & 引用名 = 变量名;
注:引用必须在定义同时初始化,而且在初始化以后所绑定的目标变量不能再做修改.
注:引用类型和绑定目标变量类型要一致。
eg:
int a = 10;
int & b = a;//b就是a的别名
b++;
cout << a << endl;//11
a++;
cout << b << endl;//12
int c = 123;
b = c;//仅是赋值,只是改变了b的值而已,并没有改变绑定目标,&b=c这样才是改变绑定目标
cout << a << endl;//a,b的值都是123
#include
using namespace std;
int main(void){
int a = 10;
int& b = a;//b引用a,b就是a的别名
cout << "a=" << a << ",b=" << b << endl;
cout << "&a=" << &a << ",&b=" << &b<<endl;//证明其实 两个地址 就是一个地址
b++;
cout << "a=" << a << ",b=" << b << endl;
//int& r;//error,引用定义时必须初始化
int c = 20;
b = c;//ok,但不是修改引用目标,仅是赋值
cout << "a=" << a << ",b=" << b << endl;
cout << "&a=" << &a << ",&b=" << &b<<endl;
//char& rc = c;//error,引用类型和目标要一致
return 0;
}
2 常引用
1)定义引用时可以加const修饰,即为常引用,不能通过常引用修改目标变量.
const 类型 & 引用名 = 变量名;
类型 const & 引用名 = 变量名;//和上面等价
int a = 10;
const int& b = a;//b就是a常引用
cout << b << endl;//10
b++;//error
#include
using namespace std;
int main(void){
//int& r1 = 100;//error
const int& r1 = 100;//ok
cout << r1 << endl;//100
int a=10,b=20;
//int& r2 = a+b;//error
//r2引用的是a+b表达式结果的临时变量(右值)
const int& r2 = a+b;
cout << r2 << endl;//30
return 0;
}
2)普通引用也可以称为左值引用,只能引用左值;而常引用也可以称为万能引用,既可以引用左值也可以引用右值。
3)关于左值和右值
左值(lvalue):可以放在赋值表达式左侧,可以被修改
右值(rvalue):只能放在赋值表达式右侧,不能被修改
#include
using namespace std;
int func(void){
int num = 123;
cout << "&num:" << &num << endl;
return num;//临时变量=num
}
int main(void){
int c = 100;
//1)将c转换为char,转换结果保存到临时变量
//2)rc实际要引用不是c而是临时变量
//char& rc = c;//error
const char& rc = c;//ok
cout << "&c:" << &c << endl;
cout << "&rc:" << (void*)&rc << endl;
//rf = 临时变量
//int& rf = func();//error
const int& rf = func();
cout << "&rf:" << &rf << endl;
cout << rf << endl;//123
return 0;
}
3 引用型函数参数
1)可以将引用用于函数的参数,这时形参就是实参别名,可以通过形参直接修改实参的值;同时还能避免函数调用时传参的开销,提高代码执行效率。
2)引用型参数有可能意外修改实参的值,如果不希望修改实参,可以将形参声明为常引用,提高效率的同时还可以接收常量型的实参
#include
using namespace std;
struct Teacher{
char name[100];
int age;
double salary;
};
//常用就是const修饰,避免被修改
void printInfo(const Teacher& t){
cout << t.name << "," << t.age/*++*/<< ","
<< t.salary << endl;
}
int main(void){
const Teacher minwei={"???",45,80000.5};
printInfo(minwei);
printInfo(minwei);
return 0;
}
4 引用型函数返回值
1)可以将函数的返回类型声明为引用,这时函数的返回结果就是return后面数据的别名,可以避免返回值带来的开销,提高代码执行效率.
2)如果函数返回类型是左值引用,那么函数调用表达式结果就也将是一个左值。
注:不要从函数中返回局部变量的引用,因为所引用的内存会在函数返回以后被释放,使用非常危险!可以在函数中返回成员变量、静态变量、全局变量或者动态内存分配的引用。
int& func(void){
...
return num;
}
func() = 100;//ok
#include
using namespace std;
struct A{
int data;
int& func(void){
return data;
}
int& func2(void){
int num = 100;
return num;//危险!!
}
};
int main(void){
A a = {100};
cout << a.data << endl;//100
//a.data = 200
a.func() = 200;//ok
cout << a.data << endl;//200
return 0;
}
5 引用和指针
1)如果从C语言角度看待引用的本质,可以认为引用就是通过指针实现的,但是在C++开发中,推荐使用引用,而不推荐使用指针.
int i = 100;
int* const pi = &i;
int& ri = i;
pi <=等价=> ri
2)指针可以不做初始化,其目标可以在初始化以后随意改变(指针常量除外),而引用必须做初始化,而且一旦初始化所引用的目标不能再改变.
int a=10,b=20;
int p;//ok
p = &a;
p = &b;
--------------------
int& r;//error
int& r = a;
r = b;//ok,但不是修改引用目标,仅是赋值运算
//下面内容了解
3)可以定义指针的指针(二级指针),但是不能定义引用的指针.
int a = 10;
int* p = &a;
int** pp = &p;//二级指针
------------------------
int& r = a;
int&* pr = &r;//error,引用的指针
int* pr = &r;//ok,仅是普通指针
4)可以指针的引用(指针变量别名),但是不能定义引用的引用。
int a = 100;
int* p = &a;
int* & rp = p;//ok,指针的引用
-----------------------------
int& r = a;
int& & rr = r;//error,引用用的引用
int& rr = r;//ok,但是仅是一个普通引用
5)可以指针数组,但是不能定义引用数组
int i=10,j=20,k=30;
int* parr[3] = {&i,&j,&k};//ok,指针数组
int& rarr[3] = {i,j,k};//error
6)可以定义数组引用(数组别名)
int i=10,j=20,k=30;
int arr[3] = {i,j,k};
int (&rarr)[3] = arr;//ok, 数组引用
7)和函数指针类似,也可以定义函数引用(函数别名)
void func(int i){}
int main(void){
void (*pf)(int) = func;//函数指针
void (&rf)(int) = func;//函数引用
pf(100);
rf(100);
}
1.#include <iostream>
2.using namespace std;
3.
4.int main(void){
5. int* pi = NULL;
6. //char c = (long)pi;//C风格强转
7. char c = long(pi);//C++风格强转
8.
9. //静态类型转换
10. //char c2 = static_cast(pi);//不合理
11. void* pv = pi;
12. pi = static_cast<int*>(pv);//合理
13. return 0;
14.}
1.#include <iostream>
2.using namespace std;
3.int main(void){
4. //volatile是标准C语言的关键字
5. //被volatile修饰的变量表示易变的,告诉编译
6. //器每次使用该变量时,都要小心从内存中读取,
7. //而不是取寄存器的副本,防止编译器优化引发
8. //的错误结果.
9. volatile const int i = 100;
10. int* pi = const_cast<int*>(&i);
11. *pi = 200;
12. cout << "i=" << i << ",*pi=" << *pi
13. << endl;//200,200
14. cout << "&i=" << (void*)&i << ",pi=" <<
15. pi << endl;
16.
17. return 0;
18.}
1.#include <iostream>
2.using namespace std;
3.int main(void){
4. //"\000"-->'\0'
5. char buf[] = "0001\00012345678\000123456";
6. struct HTTP{
7. char type[5];
8. char id[9];
9. char passwd[7];
10. };
11. HTTP* pt = reinterpret_cast<HTTP*>(buf);
12. cout << pt->type << endl;//0001
13. cout << pt->id << endl;//12345678
14. cout << pt->passwd << endl;//123456
15.
16. return 0;
17.}