原创 C++ 学习之路(五) 类的构成

2014-2-17 15:45 1284 11 16 分类: 软件与OS 文集: C++ 学习之路

类(Classes)开明

      类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为

class ,其功能与 C 语言中的 struct 类似,不同之处是 class 可以包含函数,而不像

struct 只能包含数据元素。(请注意这里是相对于C语言来说的,C++中struct 和 class关

键字类似,仅在于默认的访问权限不同)

类定义的形式是:

class class_name {

permission_label_1:

member1;

permission_label_2:

member2;

...

} object_name;

其中 class_name 是类的名称 (用户自定义的类型) ,而可选项 object_name 是一个或

几个对象(object)标识。Class 的声明体中包含成员 members,成员可以是数据或函数

定义,同时也可以包括允许范围标志 permission labels,范围标志可以是以下三个关键

字中任意一个:private:, public: 或 protected:。它们分别代表以下含义:

•private :class 的 private 成员,只有同一个 class 的其他成员或该 class 的

“friend” class 可以访问这些成员。

 

•protected :class 的 protected 成员,只有同一个 cl ass 的其他成员,或该 class

的“friend” class,或该 class 的子类(derived classes) 可以访问这些成员。

 

•public :class 的 public 成员,任何可以看到这个 class 的地方都可以访问这

些成员。

如果我们在定义一个 class 成员的时候没有声明其允许范围,这些成员将被默认为

private 范围。

例如:

class CRectangle {

int x, y;

public:

void set_values (int,int);

int area (void);

} rect;

上面例子定义了一个 class CRectangle 和该 class 类型的对象变量 rect 。这个 class

有 4 个成员:两个整型变量 (x 和 y) ,在 private 部分 (因为 private 是默认的允许

范围);以及两个函数, 在 public 部分:set_values() 和 area(),这里只包含了函

数的原型(prototype)。

       注意 class 名称与对象(object)名称的不同:在上面的例子中,CRectangle 是 class

名称 (即用户定义的类型名称),而 rect 是一个 CRectangle 类型的对象名称。它们的区

别就像下面例子中类型名 int 和 变量名 a 的区别一样:

Int  a;

int 是 class 名称 (类型名) ,而 a 是对象名 object name (变量)。

在程序中,我们可以通过使用对象名后面加一点再加成员名称(同使用 C structs 一样),

来引用对象 rect 的任何 public 成员,就像它们只是一般的函数或变量。例如:

rect.set_value (3,4);

myarea = rect.area();

但我们不能够引用 x 或 y ,因为它们是该 class 的 private 成员,它们只能够在该

class 的其它成员中被引用。晕了吗?下面是关于 class CRectangle 的一个复杂的例子:

// classes xample

#include

class CRectangle {

int x, y;

public:

void set_values (int,int);

int area (void) {return (x*y);}

};

void CRectangle::set_values (int a, int

b) {

x = a;

y = b;

}

int main () {

CRectangle rect;

rect.set_values (3,4);

cout << "area: " << rect.area();

}

area: 12

 

      上面代码中新的东西是在定义函数 set_values().使用的范围操作符(双冒号:: )。它是

用来在一个 class 之外定义该 class 的成员。注意,我们在 CRectangle class 内部已经

定义了函数 area() 的具体操作,因为这个函数非常简单。而对函数 set_values() ,在

class 内部只是定义了它的原型 prototype,而其实现是在 class 之外定义的。这种在

class 之外定义其成员的情况必须使用范围操作符::。

范围操作符 (: 声明了被定义的成员所属的 class 名称,并赋予被定义成员适当的范

围属性,这些范围属性与在 class 内部定义成员的属性是一样的。例如,在上面的例子

中,我们在函数 set_values() 中引用了 private 变量 x 和 y,这些变量只有在 class

内部和它的成员中才是可见的。

在 class 内部直接定义完整的函数,和只定义函数的原型而把具体实现放在 class 外部

的唯一区别在于,在第一种情况中,编译器(compiler) 会自动将函数作为 inline 考虑,

而在第二种情况下,函数只是一般的 class 成员函数。

我们把 x 和 y 定义为 private 成员 (记住,如果没有特殊声明,所有 class 的成员均

默认为 private ),原因是我们已经定义了一个设置这些变量值的函数

(set_values()) ,这样一来,在程序的其它地方就没有办法直接访问它们。也许在一个

这样简单的例子中,你无法看到这样保护两个变量有什么意义,但在比较复杂的程序中,

这是非常重要的,因为它使得变量不会被意外修改 (这里意外指的是从 object 的角度

来讲的意外)。

       使用 class 的一个更大的好处是我们可以用它来定义多个不同对象(object)。例如,

接着上面 class CRectangle 的例子,除了对象 rect 之外,我们还可以定义对象 rectb :

// class xample

#include

class CRectangle {

int x, y;

public:

void set_values (int,int);

int area (void) {return (x*y);}

};

void CRectangle::set_values (int a, int

b) {

x = a;

y = b;

}

int main () {

CRectangle rect, rectb;

rect.set_values (3,4);

rectb.set_values (5,6);

cout << "rect area: " << rect.area()

<< endl;

cout << "rectb area: " << rectb.area()

<< endl;

}

rect area: 12

rectb area: 30

 

注意: 调用函数 rect.area() 与调用 rectb.area()所得到的结果是不一样的。这是因为

每一个 class CRectangle 的对象都拥有它自己的变量 x 和 y,以及它自己的函数

set_value() 和 area()。

      这是基于对象( object) 和 面向对象编程 (object-oriented programming)的概念的。

这个概念中,数据和函数是对象(object)的属性(properties),而不是像以前在结构化

编程 (structured programming) 中所认为的对象(object)是函数参数。在本节及后面

的小节中,我们将讨论面向对象编程的好处。

在这个具体的例子中,我们讨论的 class (object 的类型)是 CRectangle,有两个实例

(instance),或称对象(object):rect 和 rectb,每一个有它自己的成员变量和成员函

数。

构造函数和析构函数 (Constructors and destructors)

      对象(object)在生成过程中通常需要初始化变量或分配动态内存,以便我们能够操作,

或防止在执行过程中返回意外结果。例如,在前面的例子中,如果我们在调用函数

set_values( ) 之前就调用了函数 area(),将会产生什么样的结果呢?可能会是一个不

确定的值,因为成员 x 和 y 还没有被赋于任何值。

为了避免这种情况发生,一个 class 可以包含一个特殊的函数:构造函数 constructor,

它可以通过声明一个与 class 同名的函数来定义。当且仅当要生成一个 class 的新的实

例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该 class 的一个对象分

配内存的时候,这个构造函数将自动被调用。下面,我们将实现包含一个构造函数的

CRectangle :

// class xample

#include

class CRectangle {

int width, height;

public:

CRectangle (int,int);

int area (void) {return

(width*height);}

};

CRectangle::CRectangle (int a, int b) { //构造函数没有返回值

width = a;

height = b;

}

int main () {

CRectangle rect (3,4);

CRectangle rectb (5,6);

cout << "rect area: " << rect.area()

<< endl;

cout << "rectb area: " << rectb.area()

<< endl;

}

rect area: 12

rectb area: 30

     正如你所看到的,这个例子的输出结果与前面一个没有区别。在这个例子中,我们只

是把函数set_values换成了class的构造函数constructor。注意这里参数是如何在class

实例 (instance)生成的时候传递给构造函数的:

CRectangle rect (3,4);

CRectangle rectb (5,6);

同时你可以看到,构造函数的原型和实现中都没有返回值(return value),也没有 void

类型声明。构造函数必须这样写。一个构造函数永远没有返回值,也不用声明 void,就

像我们在前面的例子中看到的。

         析构函数 Destructor 完成相反的功能。它在 objects 被从内存中释放的时候被自动

调用。释放可能是因为它存在的范围已经结束了(例如,如果 object 被定义为一个函数

内的本地(local)对象变量,而该函数结束了);或者是因为它是一个动态分配的对

象,

而被使用操作符 delete 释放了。

析构函数必须与 class 同名,加水波号 tilde (~) 前缀,必须无返回值。

析构函数特别适用于当一个对象被动态分别内存空间,而在对象被销毁的时我们希望释

放它所占用的空间的时候。例如:

// example on constructors and

destructors

#include

class CRectangle {

int *width, *height;

public:

CRectangle (int,int);

~CRectangle ();

int area (void) {return (*width *

*height);}

};

CRectangle::CRectangle (int a, int b) {

width = new int;

height = new int;

*width = a;

*height = b;

}

CRectangle::~CRectangle () {

delete width;

delete height;

}

int main () {

CRectangle rect (3,4), rectb (5,6);

cout << "rect area: " << rect.area()

<< endl;

cout << "rectb area: " << rectb.area()

<< endl;

return 0;

}

rect area: 12

rectb area: 30

 

构造函数重载(Overloading Constructors)

像其它函数一样,一个构造函数也可以被多次重载(overload)为同样名字的函数,但有

不同的参数类型和个数。记住,编译器会调用与在调用时刻要求的参数类型和个数一样

的那个函数(Section 2.3, Functions-II)。在这里则是调用与类对象被声明时一样的那

个构造函数。

PARTNER CONTENT

文章评论5条评论)

登录后参与讨论

用户1711475 2015-4-15 10:33

还是那个老问题,如果整容了,是不是就无法使用了。

用户1739951 2015-4-1 17:27

嗯,欧姆龙的我也试了,还不错。

用户1221358 2015-4-1 08:07

慕尼黑电子展上,我尝试了欧姆龙的视觉识别,能识别年龄、性别和表情,还挺准的。

用户403664 2014-2-17 08:51

抱歉,游客和第一次发贴都要审核,但是之后就OK了。BTW,您真是加拿大的吗。。。

用户579229 2014-2-15 11:10

游客不能发表评论,专门注册上来讨论着一篇: 楼主这篇文章没有看完,开头就太幼稚了。 第一处错误: class和struct的区别大错而特错了:他们的区别绝对不是包含函数的问题,而是,在默认情况下,struct的member是公有的public属性;而默认情况下,class的members是私有的private属性。 其次,struct理所当然的可以包含函数。我可以绝对有把握的说楼主根本就没有研究过linux内核。各种大量的file_operations结构体,都是一个个的函数指针,从某种意义上来讲,和class定义函数方法是一模一样的,并且,struct利用定义函数指针的方法来隐藏方法。 第二处明显错误: private的用法和理解明显的错误,根本就不是自己class的成员来访问,因为楼主根本就不理解class和object的含义。一个定义的类用constructor初始化这个类的一个object以后,这个object通过自己的成员函数来访问自己的私有成员。 这个和楼主的理解大相径庭。还有,请问一个类的friend函数能不能访问这个类的私有成员,还是只有这个类的friend class能访问这个类的私有成员。继承类能不能访问这个类的私有成员? 漏洞百出,漏洞百出,漏洞百出。 后面就不看了。 希望看到这篇博文的学员不要被此文章误导,造成理解上的巨大偏差,赶紧跳过。 谢谢。

用户377235 2014-2-15 11:06

游客是否都要人工审核?

用户377235 2014-2-15 11:05

这是第二次评论,测试看看游客能否评论,上一次评论如下: 楼主这篇文章没有看完,开头就太幼稚了。 第一处错误: class和struct的区别大错而特错了:他们的区别绝对不是包含函数的问题,而是,在默认情况下,struct的member是公有的public属性;而默认情况下,class的members是私有的private属性。 其次,struct理所当然的可以包含函数。我可以绝对有把握的说楼主根本就没有研究过linux内核。各种大量的file_operations结构体,都是一个个的函数指针,从某种意义上来讲,和class定义函数方法是一模一样的,并且,struct利用定义函数指针的方法来隐藏方法。 第二处明显错误: private的用法和理解明显的错误,根本就不是自己class的成员来访问,因为楼主根本就不理解class和object的含义。一个定义的类用constructor初始化这个类的一个object以后,这个object通过自己的成员函数来访问自己的私有成员。 这个和楼主的理解大相径庭。还有,请问一个类的friend函数能不能访问这个类的私有成员,还是只有这个类的friend class能访问这个类的私有成员。继承类能不能访问这个类的私有成员? 漏洞百出,漏洞百出,漏洞百出。 不忍直视。后面就不看了。 希望看到这篇博文的学员不要被此文章误导,造成理解上的巨大偏差,赶紧跳过。 谢谢。

用户377235 2014-2-15 08:58

楼主这篇文章没有看完,但是,开头就太幼稚了。 第一处错误: class和struct的区别大错而特错了:他们的区别绝对不是包含函数的问题,而是,在默认情况下,struct的member是公有的public属性;而默认情况下,class的members是私有的private属性。 其次,struct理所当然的可以包含函数。我可以绝对有把握的说楼主根本就没有研究过linux内核。各种大量的file_operations结构体,都是一个个的函数指针,从某种意义上来讲,和class定义函数方法是一模一样的,并且,struct利用定义函数指针的方法来隐藏方法。 第二处明显错误: private的用法和理解明显的错误,根本就不是自己class的成员来访问,因为楼主根本就不理解class和object的含义。一个定义的类用constructor初始化这个类的一个object以后,这个object通过自己的成员函数来访问自己的私有成员。 这个和楼主的理解大相径庭。还有,请问一个类的friend函数能不能访问这个类的私有成员,还是只有这个类的friend class能访问这个类的私有成员。继承类能不能访问这个类的私有成员? 漏洞百出,漏洞百出,漏洞百出。 不忍直视。后面就不看了。 希望看到这篇博文的学员不要被此文章误导,造成理解上的巨大偏差,赶紧跳过。 谢谢。
相关推荐阅读
用户420622 2014-02-17 16:01
C++学习之路(九) 多态
多态 (Polymorphism) 1.基类的指针(Pointers to base class)      继承的好处之一是一个指向子类(derived class)的指针与一个指向基类...
用户420622 2014-02-15 15:43
C++ 学习之路(八) 类之间的继承
类之间的继承     类的一个重要特征是继承,这使得我们可以基于一个类生成另一个类的对象,以便使后 者拥有前者的某些成员,再加上它自己的一些成员。例如,假设我们要声明一系列类型 的多边...
用户420622 2014-02-15 09:32
C++学习之路 (七) 类之间的关系
类之间的关系(Relationships between classes)   1.友元函数(Friend functions)      前面的章节中我们已经看到了对 class 的不同成员存...
用户420622 2014-02-14 10:01
C++学习之路(二)控制台交互
四.控制台交互(Communication through console) 在 C++的 iostream 函数库中,一个程序的标准输入输出操作依靠两种数据流:cin 给输入使用和 cout...
用户420622 2014-02-14 10:00
C++ 学习之路(一)
前言:    大家好,我是来自南京的一名大四学生,专业是电子信息工程。最近在学习Qt的过程中回顾了C++的相关知识,感觉收获很多。这是我第一次发文章,在文章中我将重点关注C++中与C的异同之处...
EE直播间
更多
我要评论
5
11
关闭 站长推荐上一条 /3 下一条