14. 类和对象提高

14.1 this指针

1)this的作用就是指向成员函数所作用的对象
2)非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针

class Complex{
public:
	double real, imag;
	void Print(){cout << real << " " << imag << endl;}
	Complex(double r, double i):real(r),imag(i){}	//初始化列表
	Complex AddOne(){
		this->real++;	//等价于real++
		this->Print();	//等价于Print()
		return *this;	//等价于返回自身
	}
};

int main(){
	Complex c1(1,1), c2(0,0);
	c2 = c1.AddOne();
	return 0;
}	//输出2,1

3)举例:
(1)不会出错:

class A{
	int i;
public:
	void Hello(){cout << "hello" << endl;}
};->void Hello(A * this){cout <<"hello" << endl;}	//翻译成C程序
int main(){
	A * p = NULL;
	p->Hello();	->Hello(p);	//执行翻译成C程序的函数
}//输出:hello

(2)会出错:

class A{
	int i;
public:
	void Hello(){cout << i <<"hello" << endl;}
};->void Hello(A * this){cout << this->i << "hello" << endl;}	//翻译成C程序
int main(){
	A * p = NULL;
	p->Hello();	->Hello(p);	//执行翻译成C程序的函数
}//程序会有问题this->i有问题

4)this指针和静态成员函数
(1)静态成员函数不能使用this指针,因为this指向某个对象,静态成员函数并不作用于某个对象
(2)因此,静态成员函数的真实参数个数就是程序中写出的参数个数
(3)例如:

//这两个不等价
static void func(int a, int b){}
static void func(A * this ,int a, int b){}

14.2 静态成员变量

1)静态成员:在说明前面加了static关键字的成员

class CRectangle{
private:
	int w,h;
	static int nTotalArea;		//静态成员变量
	static int nTotalNumber;
public:
	CRectangle(int w_, int h_):w(w_),h(h_){}
	~CRectangle(){}
	static void PrintTotal();	//静态成员函数
	
}

2)普通成员变量每个对象各自有一份,静态成员变量一共就一份,为所有对象共享
3)sizeof运算符不会计算静态成员变量

class A{
	int n;
	static int s;
};
sizeof(A)  = 4

4)普通成员函数必须作用于某个对象,静态成员函数并不具体作用于某个对象
5)因此静态成员不需要通过对象就能访问
6)如何访问静态成员:
(1)类名::成员名:CRectangle::PrintTotal();
(2)对象名.成员名
(3)指针->成员名
(4)引用.成员名

7)静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在
8)静态成员函数本质上是全局函数
9)设置静态成员这种机制目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解
10)静态成员示例:考虑一个需要随时知道矩形总数和总面积的处理程序:

#include <iostream>
using namespace std;
class CRectangle{

    int w, h;
    static int nTotalNumber;
    static int nTotalArea;
public:
    CRectangle(int w_, int h_){         //构造函数
        w = w_; h = h_;
        nTotalNumber++;
        nTotalArea += w * h;
    }
    CRectangle(const CRectangle & c){   //复制构造函数
        w = w_; h = h_;
        nTotalNumber++;
        nTotalArea += c.w * c.h;
    }
    ~CRectangle(){                      //析构函数
        nTotalNumber--;
        nTotalNumber -= w * h;
    }
    static void PrintTotal(){
        cout << "Totalnum = " << nTotalNumber<< endl;
        cout << "Totalarea = " << nTotalArea<< endl;
    }

};
//必须在定义类的文件中对静态成员变量进行一次说明或者初始化,否则编译不通过
int CRectangle::nTotalArea = 0;
int CRectangle::nTotalNumber = 0;
int main(){
    CRectangle c1(1,2),r2(3,2);    
    CRectangle c3(c2);         //调用复制构造函数
    CRectangle * pc = new CRectangle(3,3);    
    c1.PrintTotal();
    CRectangle::PrintTotal();
    //cout << CRectangle::nTotalArea << endl; //错误,因为是私有的,私有成员变量必须通过成员函数访问
    return 0;
}
输出:
Totalnum = 4
Totalarea = 19
Totalnum = 4
Totalarea = 19

11)静态成员函数不能访问非静态成员变量,也不能访问非静态成员函数

14.3 成员对象和封闭类

1)有成员对象的类叫封闭类:一个类有其他类成员变量

#include <iostream>
using namespace std;
class CTyre{        //轮胎类
    int width;
    int radius;
public:
    CTyre(int w, int r):width(w),radius(r){}
};
class CEngine{  //引擎类

};
class CCar{     //汽车类,就是封闭类
    int price;
    CTyre tyre;
    CEngine engine;
public:
    CCar(int p, int tr, int tw);  

};
CCar::CCar(int p, int tr, int tw):price(p),tyre(tr,tw){}//初始化列表形式
int main(){
    CCar car(20000,17,225);
    return 0;
}

2)如果CCar不定义构造函数,下列语句编译会出错,因为编译器不知道car.tyre怎么初始,car.engine初始化没问题,使用默认构造函数初始化即可

CCar car;

3)任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象是如何初始化的。具体做法就是通过封闭类的构造函数的初始化列表
4)成员对象初始化列表中的参数可以是任意复杂的表达式,可以包括函数、变量,只要表达式中的函数或者变量有定义就行

5)封闭类构造函数和析构函数的执行顺序
(1)封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数
(2)对象成员的构造函数调用次序和对象成员在类中的声明次序已知,与他们在初始化列表中出现的次序无关
(3)当封闭类对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数,次序和构造函数调用次序相反

(4)一般先构造的后消亡

#include <iostream>
using namespace std;
class CTyre{     //轮胎类
public:
    CTyre(){cout << "CTyre constructor" << endl;}
    ~CTyre(){cout << "CTyre destructor" << endl;}
};
class CEngine{  //引擎类
public:
    CEngine(){cout << "CEngine constructor" << endl;}
    ~CEngine(){cout << "CEngine destructor" << endl;}
};
class CCar{     //汽车类,就是封闭类
private:
    CEngine engine;
    CTyre tyre;
public:
     CCar(){cout << "CCar constructor" << endl;}
    ~CCar(){cout << "CCar destructor" << endl;}
};

int main(){
    CCar car;
    return 0;
}
输出:
CEngine constructor
CTyre constructor
CCar constructor
CCar destructor
CTyre destructor
CEngine destructor

6)封闭类和复制构造函数

class A{
public:
	A(){cout << "default" << end;}
	A(A &a){cout << "copy" << endl;}	
};
class B {A a;};
int main(){
	B b1,b2(b1);
	return 0;
}
输出:
default
copy

14.4 常量对象、常量成员函数和常引用

1)如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加上const关键字

class A{
	int val;
};

const A a;	//声明为常量对象,以后就不能修改值了

2)在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
3)常量成员函数中不能修改成员变量的值(static变量除外),也不能调用同类的非常量成员函数(static成员函数除外)

class Sample{
public:
	int val;
	void GetValue() const;
	void func(){};
	Sample(){}
};
void Sample::GetValue() const{
	value = 0;	//wrong
	func();		//wrong
}
int main(){
	const Sample s;
	s.value = 100;	//错误,常量对象不可以修改值
	s.func();		//错误,常量对象不可以调用非常量成员函数
	s.GetValue();	//正确,常量对象可以执行常量成员函数
}

4)两个成员函数,同名,一个带const一个不带,算重载,不算重复定义

class Sample{
public:
	int val;
	int GetValue() const{return n;};
	void GetValue(){return 2 * n;};
	Sample(){val = 1;}
};
int main(){
	const Sample s1;
	Sample s2;
	cout << s1.GetValue() << " " << s2.GetValue() << endl;
	return 0;
}	//分别调用const 和非const

5)常引用:可以在引用前面加const关键字,成为常引用,不能通过常引用,修改其引用变量的值
6)对象作为函数的参数时,为了避免复制以及修改对象的值,通常使用常引用作为参数

class A{};
void func(const A & a){}

14.5 友元

1)友元分为友元函数和友元类两种
(1)友元函数:一个类的友元函数可以访问该类的私有成员

#include <iostream>
using namespace std;
class CCar; //提前声明CCar,以便后面CDriver类使用
class CDriver{
public:
    void ModifyCar(CCar * pCar);    //改装汽车
};
class CCar{
private:
    int price;
    friend int MostExpensiveCar(CCar cars[], int total);    //声明全局函数int MostExpensiveCar(CCar cars[], int total);为CCar类的友元函数
    friend void CDriver::ModifyCar(CCar * pCar);//声明void ModifyCar(CCar * pCar); 函数为CCar的友元函数
};

void ModifyCar(CCar * pCar){
    pCar->price += 1000;
}
int MostExpensiveCar(CCar cars[], int total){
    int tmpMax = -1;
    for (int i = 0 i < total; i++){
        if (cars[i] > tmpMax)
            tmpMax = cars[i].price;
    }
    return tmpMax;
}

int main(){
    
    return 0;
}

(2)可以将一个类的成员函数(包括构造函数,析构函数)声明为另一个类的友元

class B{
public:
	void func();
};
class A{
	friend void B::func();	//B的成员函数func()就可以访问类A的私有成员
};

2)友元类:一个类可以把另外一个类声明为自己的友元
(1)如果A是B的友元类,那么A的成员函数可以访问B的私有成员

class A{
	int price;
	friend class B;	//声明B为友元类
};
class B {
public:
	A a;
	void modify(){a.price += 100;}	//因为B是A的友元类,所以B的成员函数可以访问A的私有成员
};
int main(){
	return 0;
}

(2)友元不可逆:A是B的友元,B不一定是A的友元
(3)友元类之间关系不能传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值