正文索引 [隐藏]

类在C++中是一个很重要的应用,十分具有学习价值。

类与对象

将数据(属性)与函数(操作)合成一个整体的行为叫封装,在计算机中用类与对象来实现。在C++的类与对象中,核心思想是“函数也可以是数据类型的成员

C++中类的定义

C++中类是一种数据类型

类的属性

在实际运用中,一种客观事物往往要用到不同的数据类型来描述它不同方面的属性。如商场中的商品:
名称:字符串;
数量:整型;
单价:浮点数;
总价:浮点数;

这里运用了三种不同数据类型的四个不同数据成员来描述同一种商品。

上述内容在C++中可以如下表示 :

class CGoods{
	private :
  		char Name[21] ;
  		int Amount ;
  		float Price ;
  		float Total_value ;
};

其中class是数据类型说明符,表示以下声明的是一个类;
“class CGoods”是类头;
花括号内的被称为类体,类体内定义了类成员表
CGoods是这个类的类型名
public关键字是一种访问限定符

访问限定符一共有三种:public、 private、protected。第一种说明的成员可以在类体外访问,后两种则不能。限定符的作用域从该符开始一直到下一个限定符前或类体结束前。
如果没有设置限定符,那么系统默认为private。private和protected都体现了类具有的封装性

类的操作

类中除了属性,更重要的部分是对类成员的操作,它们用函数来完成。

class CGoods{
	private :
  		char Name[21] ;
  		int Amount ;
  		float Price ;
  		float Total_value ;
	public :
  		void RegisterGoods(char[],int,float); //输入数据
  		void CountTotal(void) ; //计算商品总价值
  		void GetName(char[]) ; //读取商品名
  		int GetAmount(void) ; //读取商品数量
  		float GetPrice(void) ; //读取商品单价
  		float GetTotal_value(void) ; //读取商品总价值 
};

像这样就在类中引用了成员函数(或函数成员)。函数也成为了类中的一员,而类把数据(属性)与函数(操作)封装为一个整体。

四个数据成员被说明为私有的,而六个函数成员被说明为公有的。这就说明对四个数据成员进行操作只能通过六个函数成员来完成,这形成了对数据的良好保护。所有的函数成员定义了类的接口
成员函数可以直接使用类中定义的任意成员,既可以使用数据成员,也可以调用函数成员。

值得一提的是,类是一种数据类型,定义时系统不会为它分配存储空间,所以不能对类的数据成员初始化或是用任意关键字限定它们的存储类型。

成员函数的定义

前面只对成员函数作了声明,没有对它进行定义,它的格式如下:

void CGoods::RegisterGoods(char name[], int amount, float price)
{
	strcpy(Name, name);
	Amount = amount;
	Price = price;
}

其中”::”叫作用域解析运算符,它指出了该函数是哪个类的成员函数。

对象的创建与使用

对象是类的实例,类只是一个样板。如上文所说,声明一个类的时候只是告诉了编译器这个类的构造,并没有分配存储空间。使用这个样板可以在内存中开辟一个同样构造的实例:对象。

创建类的对象通常有两种方法:直接定义与动态创建。
“CGoods Car”就直接定义并创建了CGoods类的一个对象Car,同时为它分配了属于自己的存储,用以存放它的数据成员与函数成员。直接定义在编译时运行前完成建立。
第二种方法在程序运行时创建对象,此处暂不提。

系统为每一个对象独立地分配了全套内存

如果在类体外定义函数,那么情况如下

代码区公用,数据区每个对象单独分配

对象的操作符是点操作符,即在对象名称后面加点号再加上数据成员或函数成员名就行了。但是,如果在类体外的话,被访问的成员必须是公有成员,只有公有成员才能在类体外访问

//类定义见上文
int main()
{
	CGoods	car;
	char  s[21];
	int	number;
	float  pr;
}

上述例子中car的四个数据成员全是私有的,所以如果写成:
car. Name;
car. Amount;
car.Price;
car.Total_value;
那就是非法的。私有成员只能用car的公有函数来访问。

函数内联

先给出内联的相关解释:

在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。

——菜鸟教程

在类中,所有在类体中定义的函数都是默认内联的。如果在类体外定义,那么需要额外声明。

class A
{
    public:
    void Foo(int x, int y);
}
inline void A::Foo(int x, int y)
{
	... 
}

上述等价于

class A
{
    public:
    void Foo(int x, int y)
    {
    	...
    }
}

值得一提的是,inline必须与函数定义放在一起,仅仅在声明时使用inline是没有用的。
而内联也只是一种“建议”。前文说道内联是用于解决“小函数高调用”问题而设置的,同样地,如果编译器认为你的函数挺复杂,无法在调用点展开,它也不会内联。换言之,是否内联取决于编译器,而不是你。

class A
{
    public:
    //无用 
    inline void Foo(int x, int y);
}
//正确 
inline void A::Foo(int x, int y)
{
	... 
}

构造函数与析构函数

上文提到,数据成员大多是私有的,对它们进行初始化就必须使用公有函数。这个函数仅在定义对象的时候执行一次,称它为构造函数

构造函数的定义与使用

构造函数是一种特殊的公有函数成员。具有以下特点:

  1. 函数名与类名相同
  2. 构造函数没有函数返回类型声明。这里的没有指的是什么也不写,而不是void。事实上构造函数的返回值就是构造函数创建的对象。
  3. 当新的对象被建立,该类的构造函数自动被调用,也仅调用这一次。
  4. 构造函数可以被重载。严格地,说明中可以有多个构造函数,他们由不同的参数表来区分。系统在执行时按函数重载的规则来选择执行。
  5. 构造函数在类体内外定义均可。
  6. 如果类说明中没有给出构造函数,那么C++编译器会自动给出一个缺省的构造函数。

以CGoods类的构造函数为例

class CGoods
{
	private :
  		char Name[21] ;
  		int Amount ;
  		float Price ;
  		float Total_value ;
	public :
		CGoods(); 
  		void RegisterGoods(char[],int,float); //输入数据
  		void CountTotal(void) ; //计算商品总价值
  		void GetName(char[]) ; //读取商品名
  		int GetAmount(void) ; //读取商品数量
  		float GetPrice(void) ; //读取商品单价
  		float GetTotal_value(void) ; //读取商品总价值 
};

CGoods(char name[], int amount, float price)
{
	strcmp(Name, name);
	Amount = amount;
	Price = price;
	Total_value = Amount * Price;
}

CGoods(char name[], float Price)
{
	strcmp(Name, name);
	Price = price;
	Amount = 0;
	Total_value = 0.0;
}

CGoods()
{
	Name[0]='\0'; 
	Amount = 0; 
	Price = 0.0;
	Total_value = 0.0;
}

可以看见三个参数、两个参数与没有参数的构造函数可以被同时定义(即重载)。它们的使用如下↓

//使用三个参数构造 
	CGoods car1("夏利2000", 30, 98000.0);
	//它等价于↓ 
	CGoods car1 = CGoods("夏利2000", 30, 98000.0);
	//使用两个参数构造
	CGoods car2("桑塔那2000", 164000.0);
	//不用任何参数构造;
	CGoods car3; 

析构函数的定义

与构造函数相对应地,如果一个对象结束使用,编译器也会自动调用一个函数来注销该对象并完成善后,这个特殊的函数成员就被称为析构函数。

  1. 析构函数名依旧与类名相同,但需要在前面加上”~”。
  2. 与构造函数相同,它没有返回类型。不同的是,析构函数没有参数。
  3. 与构造函数不同,一个类有且仅有一个析构函数。但是它可以缺省。

以下给出一个定义矩形类的例子。

#include <cstdio>
#include <iostream>

using namespace std;

class Rectangle 
{
	int left, top, right, bottom;
	public:
		//缺省构造函数必须在此指定缺省实参
		Rectangle(int = 0, int = 0, int = 0, int = 0); 
		~Rectangle(){}; //析构函数,在此函数体为空
		void Assign(int , int , int , int );
		void SetLeft(int t){ left = t;}
 		void SetRight( int t ){ right = t;}
		void SetTop( int t ){ top = t;}
		void SetBottom( int t ){ bottom = t;}
		void Show();
};

Rectangle::Rectangle(int l , int t, int r, int b) 
{
	left = l; 
  	top = t;
  	right = r; 
  	bottom = b; 
}
void Rectangle::Assign(int l, int t, int r, int b)
{
  	left = l; 
  	top = t;
  	right = r; 
  	bottom = b; 
}
void Rectangle::Show()
{
	cout << "left-top point is (" << left << "," << top << ")" << endl;
	cout << "right-bottom point is (" << right << "," << bottom << ")" << endl; 
}

int main()
{
	Rectangle rect; 
	rect.Show();
	rect.Assign(100,200,300,400);
	rect.Show();
	Rectangle rect1(0,0,200,200);
	rect1.Show();
	Rectangle rect2(rect1);
	rect2.Show();
	return 0;
}

引用与拷贝构造函数

引用