C++面向对象程序设计(三)——1.从C到C++
795
2022-05-30
C++面向对象程序设计(三)——3.类和对象提高
本文是中国大学MOOC,北京大学程序设计与算法(三)C++面向对象程序设计第三周笔记。本课程学习的github仓库欢迎Fork
本文目录
C++面向对象程序设计(三)——3.类和对象提高
一 this 指针
C++ 到 C程序的翻译
二 静态成员
基本概念
如何访问静态成员
1.类型::成员名
2.对象名.成员名
3.指针->成员名
4.引用.成员名
三 成员对象和封闭类
基本概念
封闭类构造函数和析构函数的执行顺序
四 友元
友元函数
友元类
五 常量成员函数
六 mutable成员变量
一 this 指针
C++ 到 C程序的翻译
//C++ class CCar{ public: int price; void SetPtice(int p); }; void CCar::SetPrice( int p ){ price = p; } int main(){ CCar car; car.SetPrice( 20000 ); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//C struct CCar{ int price; }; void SetPrice( struct CCar * this, int p) { this -> price = p; } int main(){ struct CCar car; SetPrice( & car, 20000 ); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
对比一下这两个程序,我们可以看到:
每个类的非静态成员函数中都隐含包含一个this指针,类型为当前类类型的指针类型
this 作用就是指向成员函数所作用的对象。在非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针。
我们可以看一个案例:
class Complex{ public: double real,imag; void Print(){ cout << real << "," << imag; } 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
需要注意的是:
静态成员函数中不能使用this指针,因为静态成员函数并不具体作用于某个对象。所以,静态成员函数的真实参数的个数,就是程序中写出的参数个数。
然而,类的非静态成员函数,真实的参数比所写的参数多1,多的这个就是this指针。
二 静态成员
基本概念
在定义前面加了static关键字的成员
class CRectangle { private: int w, h; static int nTotalArea; //静态成员变量 static int nTotalNumber; public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); //静态成员函数 };
1
2
3
4
5
6
7
8
9
10
11
静态成员变量为所有对象共享,且sizeof运算符不会计算静态成员变量
class CMyclass{ int n; static int s; }; //sizeof(CMyclass) 等于 4
1
2
3
4
5
普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享
普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。所以实际上静态成员不需要通过对象就能访问。
如何访问静态成员
CRectangle::PrintTotal();
1
CRectangle r; r.PrintTotal();
1
2
CRectangle * p = &r; p -> PrintTotal();
1
2
CRectangle & ref = r; int n = ref.nTotalNumber;
1
2
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在
静态成员函数本质上是全局函数,主要目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去想一个整体,易于维护和理解
class CRectangle { private: int w, h; static int nTotalArea; //静态成员变量 static int nTotalNumber; public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); //静态成员函数 }; CRectangle::CRectangle(int w_,int h_ ) { w = w_; h = h_; nTotalNmuber ++; nTotalArea += w * h; } CRectangle::~CRectangle() { nTotalNumber --; nTotalArea -= w * h; } void CRectangle::PrintTotal() { cout << nTotalNumber << "," < 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 要注意的是:在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数 必须在类外面对静态函数变量进行说明或初始化 void CRectangle::PrintTotal() { cout << w << "," << nTotalNumber << "," << nTotalArea << endl; //error } CRectangle::PrintTotal(); //解释不通,w到底属于哪个对象? 1 2 3 4 5 三 成员对象和封闭类 基本概念 有成员对象的类叫封闭类 class CTyre { //轮胎类 private: int radius; //半径 int width; //宽度 public: CTyre( int r, int w ):radius(r),width(w){}//初始化列表,为成员变量指定初始值 }; class CEngine{ //引擎类 }; class CCar{ //汽车类 private: int price; //价格 CTyre yre; CEngine engine; public: CCar( int p, int tr, int tw ); }; CCar::CCar( int p, int tr, int w):price(p),tyre(tr,w) {}; int main() { CCar car( 20000, 17, 225 ); return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 上面的例子中,如果CCar类不定义构造函数,那么下面的语句编译会出错 CCar car; 因为编译器不明白car.type,该如何初始化。car.engine的初始化没问题,用默认构造函数就可以了。 任何生成封闭类对象的语句,都要使得编译器明白,对象中的成员对象,是如何初始化的。完成这一任务的方法是通过封闭类的构造函数的初始化列表,成员对象初始化列表中的参数可以是任意复杂的表达式,可以包括函数,变量,只要表达式中的函数或变量有定义就行。 封闭类构造函数和析构函数的执行顺序 封闭类对象生成时,先所有成员对象的构造函数,然后才执行封闭类的构造函数 对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与成员初始化列表次序无关 封闭类对象消亡时,先执行封闭类析构函数,再执行成员对象的析构函数。次序和构造函数的调用次序相反 class CTyre{ public: CTyre(){cout << " Ctyre contructor "<< endl; } ~CTyre(){cout << " Ctyre destructor "<< endl; } } class CEngine{ public: CEngine(){cout << "CEngine contructor" << 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 contructor //Ctyre contructor //CCar constructor //CCar destructor //Ctyre destructor //CEngine destructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 封闭类的对象如果用默认复制构造函数初始化,那么它包含的成员对象也会用复制构造函数初始化 class A{ public: A(){cout << "default" << endl;} A(A & a){ cout << "Copy" << endl;} }; class B {A a;}; int main(){ B b1, b2(b1); return 0; } //输出: //default //Copy //b2.a是用类A的复制构造函数初始化的。 //调用复制构造函数的实参是b1.a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 四 友元 友元函数 一个类的友元函数可以访问该类的私有成员 class CCar; class CDriver{ public: void ModifyCar(CCar * pCar); }; class CCar { private: int price; friend int MostExpensiveCar( CCar cars[], int total ); //声明友元 friend void CDriver::ModifyCar( CCar * pCar ); //声明友元 } void CDriver::ModifyCar( CCar * pCar ) { pCar->price += 1000 ; //改装后变贵了 } int MostExpensiveCar( CCar cars[],int total ) //最贵汽车价格 { int tmpMax = -1; for( int i = 0; i < total ; ++ i ) { if(car[i].price > tmpMax) tmpMax = cars[i].price; } return tmpMax; } int main(){ return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 当然我们还可以把一个类的成员函数(包括析构,构造等)说明为另一个类的友元 class B{ public: void function(); }; class A{ friend void B::function(); } 1 2 3 4 5 6 7 8 友元类 如果A是B的友元类,那么A的成员函数可以访问B的私有成员 class CCar{ private: int price; friend class CDriver; //声明CDriver为友元类 }; class CDriver{ public: CCar myCar; void ModifyCar(){ //改装汽车 myCar.price += 1000; //CDriver是CCar的友元类,所以可以访问CCar的私有成员 } }; int main(){ return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 友元类之间的关系不能传递,不能继承 五 常量成员函数 如果不希望某个对象的值被改变,那么可以在对象前加const关键字 class Sample{ private: int value; public: Sample(){} void SetValue(){} }; const Sample Obj; //常量对象 Obj.SetValue (); //错误,常量对象只能使用构造函数,析构函数,有const说明的函数 1 2 3 4 5 6 7 8 9 10 类的成员函数说明后面可以加const关键字,该成员函数成为常量成员函数 常量成员函数内部不能改变属性的值,也不能调用非常量成员函数 在定义和声明成员函数时都应该用const关键字 如果一个成员函数中没有调用非常量成员函数,也没有修改成员函数变量的值,那么最好将其写成常量成员函数 如果两个函数,名字参数表都一样,但是一个有const,一个没有,算重载 六 mutable成员变量 可以在const成员函数中修改的成员变量 class CTest{ public: bool GetData() const { m_n1++; return m_b2; } private: mutale int m_n1; bool m_b2; }; 1 2 3 4 5 6 7 8 9 10 11 C++ 面向对象编程
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。