C++面向对象程序设计(三)——3.类和对象提高
【摘要】
C++面向对象程序设计(三)——3.类和对象提高
本文是中国大学MOOC,北京大学程序设计与算法(三)C++面向对象程序设计第三周笔记。本课程学习的github仓库欢迎Fork
本文目录
C+...
C++面向对象程序设计(三)——3.类和对象提高
本文是中国大学MOOC,北京大学程序设计与算法(三)C++面向对象程序设计第三周笔记。本课程学习的github仓库欢迎Fork
一 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;
}
//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;
}
对比一下这两个程序,我们可以看到:
每个类的非静态成员函数中都隐含包含一个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
需要注意的是:
静态成员函数中不能使用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(); //静态成员函数
};
静态成员变量为所有对象共享,且sizeof
运算符不会计算静态成员变量
class CMyclass{
int n;
static int s;
};
//sizeof(CMyclass) 等于 4
普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享
普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。所以实际上静态成员不需要通过对象就能访问。
如何访问静态成员
1.类型::成员名
CRectangle::PrintTotal();
2.对象名.成员名
CRectangle r;
r.PrintTotal();
3.指针->成员名
CRectangle * p = &r;
p -> PrintTotal();
4.引用.成员名
CRectangle & ref = r;
int n = ref.nTotalNumber;
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在
静态成员函数本质上是全局函数,主要目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去想一个整体,易于维护和理解
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 << "," <<nTotalArea <<endl;
}
int CRectangle::nTotalNumber = 0;
int CRectangle::nTotalNumber = 0;
//必须在定义类的文件中对静态函数变量进行以此说明或初始化
//否则编译能通过,链接1不能通过
int main()
{
CRectangle r1( 3, 3 ), r2(2, 2);
cout << CRectangle::nTotalNumber; //error,私有
CRectangle::PrintTotal();
r1.PrintTotal();
return 0;
}
//输出:
//2,13
//2,13
要注意的是:在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数
必须在类外面对静态函数变量进行说明或初始化
void CRectangle::PrintTotal()
{
cout << w << "," << nTotalNumber << "," << nTotalArea << endl; //error
}
CRectangle::PrintTotal(); //解释不通,w到底属于哪个对象?
三 成员对象和封闭类
基本概念
有成员对象的类叫封闭类
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;
}
上面的例子中,如果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
- 封闭类的对象如果用默认复制构造函数初始化,那么它包含的成员对象也会用复制构造函数初始化
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
四 友元
友元函数
一个类的友元函数可以访问该类的私有成员
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;
}
当然我们还可以把一个类的成员函数(包括析构,构造等)说明为另一个类的友元
class B{
public:
void function();
};
class A{
friend void B::function();
}
友元类
如果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;
}
友元类之间的关系不能传递,不能继承
五 常量成员函数
如果不希望某个对象的值被改变,那么可以在对象前加const
关键字
class Sample{
private:
int value;
public:
Sample(){}
void SetValue(){}
};
const Sample Obj; //常量对象
Obj.SetValue (); //错误,常量对象只能使用构造函数,析构函数,有const说明的函数
类的成员函数说明后面可以加const
关键字,该成员函数成为常量成员函数
常量成员函数内部不能改变属性的值,也不能调用非常量成员函数
在定义和声明成员函数时都应该用const
关键字
如果一个成员函数中没有调用非常量成员函数,也没有修改成员函数变量的值,那么最好将其写成常量成员函数
如果两个函数,名字参数表都一样,但是一个有const
,一个没有,算重载
六 mutable成员变量
可以在const
成员函数中修改的成员变量
class CTest{
public:
bool GetData() const
{
m_n1++;
return m_b2;
}
private:
mutale int m_n1;
bool m_b2;
};
文章来源: blog.csdn.net,作者:沧夜2021,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/CANGYE0504/article/details/104744289
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)