1. 从C到C++
-
引用:
-
类型名 & 引用名 = 某变量名。某个变量的引用,等价于这个变量,相当于该变量的一个别名。
int n = 4; int & r = n;// r引用了n,则相当于n为r的别名,r和n之后就是一回事了
-
引用只能引用变量,不能引用常量和表达式。
-
初始化后它就一直引用该变量,不会再引用别的变量了。
-
常引用:不能通过常引用去修改其引用的内容
int n= 100; const int & r= n; r=200;∥编译错 n=300;∥/没问题
-
-
const关键字:
- 常量指针:
- 不可通过常量指针修改其指向的内容
- 不能把常量指针赋值给非常量指针(防止通过非常量指针修改常量指针指向的内容)
- 常量指针:
-
动态内存分配:
-
P=new T;
T是任意类型名,P是类型为T*的指针。动态分配出一片大小为 sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。
-
数组:P= new T[N];
-
释放内存空间:
delete 指针; //该指针必须指向new出来的空间
delete [] 指针; //该指针必须指向new出来的数组
-
-
内联函数:
-
好处:可以提高函数执行的效率;坏处:可执行文件体积增大(以空间换时间
-
在函数定义前面加“ inline”关键字,即可定义内联函数
-
-
函数重载:
- 函数名相同而参数不同
-
函数的缺省参数(or默认参数):
-
C++中,定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。
-
void func( int x1, int X2=2, int X3=3){} func(10);∥等效于func(10,2,3) func(10,8);∥等效于func(10,8,3) func(10,,8);/不行,只能最右边的连续若干个参数缺省
-
如果某个写好的函数要添加新的参数,而原先那些调用该函数的语句未必需要使用新増的参数,那么为了避免对原先那些函数调用语句的修改,就可以使用缺省参数。
-
-
类和对象:
-
通过类,可以定义变量。类定义出来的变量,也称为类的实例,就是我们所说的“对象”。
-
和结构变量一样,对象所占用的内存空间的大小,等于所有成员变量的大小之和。
-
类的成员函数和类的定义分开写
class CRectangle{ public: int w,h; int Area();//成员函数仅在此处声明 }; int CRectangle::Area(){ return w*h; }
-
类成员的可访问范围:如果某个成员前面没有关键字,则缺省地被认为是私有成员。
-
2. 类和对象基础
- 构造函数:
- 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数
- 复制构造函数
- 只有一个参数,即对同类对象的引用。
- 如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能(浅拷贝)。
- 对象间赋值并不导致复制构造函数被调用
- 传入函数实参和函数返回对象时都会调用复制构造函数
- 类型转换构造函数:
- 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数
- 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)。
- 析构函数:
- 名字与类名相同,在前面加 ~ ,没有参数和返回值,一个类最多只能有一个析构函数。
- 析构函数在对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
- 如果定义类时没写析构函数,则编译器生成缺省析构函数。缺省析构函数什么也不做。
- 对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用。
- delete运算会导致析构函数调用。若new一个对象数组,那么用delete释放时应该写[]。否则只delete一个对象(调用一次析构函数)
- 函数调用的返回值(临时对象)被用过后,该临时对象析构函数被调用
- new出来的对象在生命期结束后不会自动调用析构函数,需要手动delete
3. 类和对象提高
- this指针
- 其作用就是指向成员函数所作用的对象
- 非静态 成员函数中可以直接使用this来代表指向该函数作用的对象的指针(静态不可以
- 静态成员变量
- 静态成员:在说明前面加了static关键字的成员。
- 访问静态成员:类名::成员名
- 必须在定义类的文件中对静态成员变量进行一次说明或初始化。否则编译能通过,链接不能通过
- 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
- 常量成员函数
- 常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
- void GetValue() const { }
- 两个成员函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
- 友元
- 友元分为友元函数和友元类两种
- 友元函数:一个类的友元函数可以访问该类的私有成员. 在函数前加上friend关键字声明友元函数。
- 可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元。
- 友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员。在类前加上friend关键字声明友元类。
- 友元类之间的关系不能传递,不能继承。a是b的友元,b是c的友元,但a不是c的友元
4. 运算符重载
-
运算符重载的基本概念:
-
运算符重载,就是对已有的运算符(C++中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。
-
运算符重载的实质是函数重载,可以重载为普通函数,也可以重载为成员函数。
-
把含运算符的表达式转换成对运算符函数的调用。
-
把运算符的操作数转换成运算符函数的参数。
-
运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
-
重载为成员函数时,参数个数为运算符目数减一。重载为普通函数时,参数个数为运算符目数。
-
返回值类型 operator 运算符(形参表) { }
-
-
赋值运算符的重载:
-
赋值运算符“=”只能重载为成员函数
-
String & operator = (const char * s){ if(this == &s){return *this};//防止a=a这样的赋值导致出错 delete [] str; str = new char[strlen(s)+1]; strcpy(str,s); return *this; }
-
-
动态数组:
-
//用以支持根据下标访问数组元素,如n=a[i]和a[i]=4这样的语句(a为对象) int & CArray::operator [] (int i) { return ptr[i]; } //返回值为int不行,不支特a[i]=4
-
class CArray { private: int size;//数组元素的个数 int* ptr;//指向动态分配的数组 public: CArray(int s=0);//s代表数组元素的个数 CArray(CArray & a);//复制构造函数 ~CArray();//析构函数 void push(int v);∥用于在数组尾部添加一个元素v CArray & operator= (const CArray & a);//用于数组对象间的赋值 int length(){return size;}/∥返回数组元素个数 int & operator[] (int i) //用以支持根据下标访问数组元素 { return ptr[i]; } }; CArray::CArray(int s):size(s) { if (s==0) {ptr = NULL;} else {ptr = new int[s];} } CArray::CArray(CArray & a) { if (!a.ptr) { ptr = NULL; size = 0; return; } ptr = new int[a.size]; memcpy(ptr,a.ptr,sizeof(int)*a.size); size = a.size; } CArray::CArray() { if (ptr) {delete [] ptr;} } //赋值号的作用是使"="左边对象里存放的数组,大小和内容都和右边的对象一样 CArray & CArray::operator=(const CArray & a) { //防止a=a这样的赋值导致出错 if (ptr==a.ptr) {return *this;} //如果a里面的数组是空的 if (a.ptr==NULL){ if (ptr) {delete [] ptr;} ptr =NULL; size = 0; return *this; } //如果原有空间够大,就不用分配新的空间 if (size < a.size) { if (ptr) {delete [] ptr;} ptr = new int[a.size]; } memcpy(ptr,a.ptr,sizeof(int)*a.size); size = a.size; return *this; } //在数组尾部添加一个元素 void CArray::push(int v) { if (ptr) { int* tmpPtr = new int[size+1];//重新分配空间 memcpy(tmpPtr,ptr,sizeof(int)*size;//拷贝原数组内容 delete [] ptr; ptr = tmpPtr; } else {ptr = new int[1];} ptr[size++]=v;//加入新的数组元素 }
-
-
自增、自减运算符的重载:
- 自增运算符、自减运算符有前置/后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:前置运算符作为一元运算符重载;后置运算符作为二元运算符重载,多写一个没用的参数。
- 前置运算符返回引用,后置运算符返回对象。
-
运算符重载注意事项:
- C++不允许定义新的运算符;
- 重载后运算符的含义应该符合日常习惯;
- 运算符重载不改变运算符的优先级;
- 以下运算符不能被重载:“.”、“.*”、“::”、“?:”、sizeof;
- 重载运算符()、[]、->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数。