c & cpp
九、 C++ 的引用 ( reference )
定义引用就是给某个变量起别名, 对引用的操作与对该变量的操作完全相同.
常引用
1) 定义引用的时候加 const 修饰, 即为常引用, 不能通过常引用修改引用的目标
2) 普通的引用只能是左值(变量), 常引用也叫万能引用,即可以引用左值也可以引用右值.关于左值和右值的概念
1) 左值: 可以放在运算符左边,可以被修改,可以被取地址
2) 右值: 只能放在赋值运算符右边, 不可以被修改, 不可以被取地址引用型的参数
1) 可以将引用用于函数的参数,可以直接修改实参变量的值,同时可以减少函数调用的开销
2) 引用参数有可能意外的修改实参变量的值,如果不希望修改实参本身,可以将行参定义为常引用,在提高传参效率的同时还可以接收常量型的实参引用型函数的返回值
1) 可以将函数的返回类型声明为引用,避免函数返回值带来的内存开销,如果一个函数的返回类型被声明为引用,那么该函数返回值可能是一个左值.
2) 为了避免在函数外部修改引用目标的变量,可以返回一个常引用.int g_data = 100; int& func(void){ g_data = 200; return g_data; } int main(void){ func() = 300; // ok --> g_data = 300 }
引用和指针
1) 引用的本质就是指针,但是建议尽量使用引用而不是指针.double d = 1.23; double& rd = d; double* const pd = &d; rd = 100; // ==> *pd = 100
2) 指针可以不初始化,指针指向的目标可以随意改变(除了指针常量),而引用必须初始化,初始化以后目标不能修改
int a, b; int* p; // ok p = &a; // p --> a p = &b; // p --> b /*错误*/int& r; // error int& r = a; // r 引用 a r = b; // 把 b 的值赋值给 a;
3) 可以定义指针的指针(二级指针),但是不能定义引用的指针
int a = 100; int* p = &a; int** pp = &p; // ok int& r = a; /*错误*/int&* pr = &r; // error
4) 可以定义指针的引用,但是不能定义引用的引用
int a;
int* p = &a;
int*& rp = p; // ok
int& r = a;
/*错误*/int&& rr = r; // error, c++11右值引用
5) 可以定义指针数组,但是不能定义引用数组,可以定义数组引用
eg:
int a, b, c;
int* parr[3] = {&a, &b, &c};
/*错误*/int& rarr[3] = {a, b, c}; // error
eg 2:
int arr[3] = {1, 2, 3};
int (&r)[3] = arr; // ok
6) 函数指针和函数引用的语法基本一样
void func(int a, double d){}
int main(void){
// 函数指针
void (*pfunc)(int, double) = func;
pfunc(100, 3.14);
// 函数引用
void (&rfunc)(int, double) = func;
rfunc(200, 4.13);
}
十、c++ 动态内存分配
对比:
c 语言: malloc() / free()
c++: new / delete 运算符
new 运算符用于分配内存, delete 运算符用于释放内存
eg:
c:
int* = (int*)malloc(4);
*p = 100;
free(p);
p = NULL;
c++
// int* = new int;
// *p = 200;
int* = new int(200); // 分配内存同时初始化
delete p;
p = NULL;
int* pa = new int[10]; // 分配 10 个 int 空间
pa[0] = 10;
pa[1] = 20;
// ...
delete[] = pa;
pa = NULL;
十一、类型转换
- 隐式类型转换
char c = 'A';
int i = c; // 隐式转换
int foo(int n){
char ch = 'A';
return ch; // 隐式转换
}
int main(void){
foo('A'); // 隐式转换
}
显示类型转换
2.1 c 语言 显式类型转换(强制类型转换)// 内存地址: 0x12345678 int* pi = (int*)0x12345678; *pi = 100; char c = 'A'; int i = (int)c; // c 中形式 int i = int(c); // c++ 中形式 int* pc = (int*)c; // 语法 ok, 逻辑不合理
2.2 c++ 兼容 c 的强制转换, 同时增加了四种操作符的形式
1) 静态类型转换
目标类型变量 = static_case<目标类型>(源类型变量);
主要用于将 void* 转换为其他类型的指针int a = 100; void* pv = &a; // int* -> void* int* pi = static_cast<int*>(pv); // ok double d = 3.14; /*错误*/pi = static_cast<int*>(&d); // 逻辑不合理, error
2) 动态类型转换
目标类型变量 = dynamic_cast<目标类型>(源类型变量);3) 常类型转换
目标类型变量 = const_cast<目标类型>(源类型变量);
用于去除一个指针或者引用的常属性.
int main(void){
int a = 100;
const int* = &a;
// *p = 20; // error
int* p2 = const_cast<int*>(p);
const int& r = a;
/*错误*/r = 30; // error
int& r2 = const_cast<int&>(r);
r2 = 30; // ok
}
4) 重解释类型转换
目标类型变量 = reinterpret_cast<目标类型>(源类型变量);
--> 任意类型指针或引用之间的转换
--> 在指针和整形数之间的转换
int a;
int* pa = &a;
double* pd = reinterpret_cast<double*>(pa);
// eg 2:
// 向内存"ox23e00000"存放数据100
// c
*((int*)0x23e00000) = 100;
// c++
int p = 0x23e00000;
int* paddr = reinterpret_cast<int*>(p);
*paddr = 100;
c++ 编程的几点小建议:
1. 慎用宏
#define PAI 3.14 ==> const double PAI = 3.14;
#define STATE_SLEEP 0
#define STATE_RUN 1
#define STATE_STOP 2
// ==>
enum STATE{SLEEP, RUN, STOP}
#define max(a, b)((a) > (b) ? (a) : (b))
// ==>
inline int max(int a, int b){
return a > b ? a : b;
}
2. 变量随用随声明同时初始化
3. 尽量使用 new / delete 取代 malloc / free
4. 少用 void* , 指针计算、联合体、强制转换
5. 尽量用 string 表示字符串, 少用 c 风格的 char*
十二、类和对象
对象
万物皆对象, 任何一种事物都可以看做是一个对象面向对象
2.1 如何描述和表达对象
通过对象的属性(名词、数量词、形容词)和行为(动词)来描述和表达对象.
“冰箱”
冰箱属性: 品牌、容量、颜色、功耗
冰箱的行为: 装东西、冷冻、冷藏
2.2 面向对象程序设计
对自然世界的观察引入到编程实践的一种理念和方法;
数据抽象: 在描述对象时, 把细节东西剥离出去, 只考虑一般性的、有规律的、统一的东西.类
类是将多个对象的共性提取出来定义的一种新的数据类型,是对对象的属性和行为的抽象描述,对象是类的实例化.
学生:
现实世界 类 虚拟世界(编程模拟)
具体对象 – 抽象 –> 属性 / 行为 – 实例化 –> 具体对象
struct Student{
string name;
int age;
int id;
void eat( ){
}
void sleep( ){
}
}
Student s;
s.name = "Leon";
s.age = 25;
s.id = 10010;
s.eat();
十三、类的定义和实例化
类的语法形式
class / struct 类型 : 继承表{
访问控制限定符:
类名(行参表) : 初始化{} // 构造函数
~类名(void){ }// 析构函数
返回类型 函数名(行参表){ } // 成员函数
数据类型 变量名; // 成员变量
};访问控制限定符
public: 公有成员, 在类的内部和外部都可以访问的成员
private: 私有成员, 只有在类的内部才能访问
protected: 保护成员
注: class 定义的类默认的访问控制属性为 private, 而 struct 定义的类默认访问属性为 public