摘要

介绍了C++中对象与类的基本概念与实践,从对象的定义、属性与行为,到类的构造/析构、访问控制、友元机制及组合关系。

目录

[TOC]

对象

基本概念

在现实世界中,对象就是我们认识世界的基本单元。
对象可以是:

  • 有形的具体存在的事物
    例如:一辆车、一个球、一个小学生;
  • 无形的、抽象的事件
    例如:一次演出、一场球赛等。

 特性

  1. 每一个对象必须有一个名字以区别其它对象;
  2. 用属性来描述对象的某些特征;
  3. 有一组操作,每一个操作决定对象的一种行为;
  4. 对象的操作可以分为两类:
    1) 自身所承受的操作
    2) 施加于其他对象的操作

例子

对象名:洗衣机
对象的属性
生产厂家:海尔
机器编号:JSG14
出厂日期:2008.05.06
对象的操作(行为)
启动、暂停、选择

面向对象程序设计中的对象

对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体。
对象可以认为是:数据+操作
在面向对象程序设计中,用数据来体现上面提到的“属性”,用函数来实现对数据的操作,以实现某些功能。

概念

“类”是对一组具有共同的属性和行为的对象的抽象。

例子

教师黎明是一个对象。

对象名: 黎明

对象的属性:

年龄:45

学历:博士

职称:教授

对象的行为(操作):

走路、吃饭、授课

一个个的象黎明这样的教师就构成教师类。

类和对象之间的关系

即抽象和具体的关系。

通常我们把具有共同属性和行为的事物所构成的集合叫做类。
在C++中可以把相同数据结构和相同操作集的对象看成属于同一类。

类是对多个对象进行抽象的结果,一个对象是类的一个实例。

  • 调用对象中的函数就是向该对象传送一个消息,要求该对象实现某一行为
    (功能、操作)。
  • 对象所能实现的行为(操作),在程序设计方法中称为方法,方法中定义了一系列的操作步骤,它们是通过调用相应的函数来实现的。
  • 实际上:在C++语言中,方法是通过成员函数来实现的。

在面向对象程序设计中的类

类是具有相同的数据和操作(函数)的一组对象的抽象描述。
在C++语言中把类中的数据称为数据成员
类中的操作是用函数来实现的,这些函数称为成员函数

面向对象程序设计的基本特征

1. 抽象

抽象的过程是将有关事物的共性归纳、集中的过程。

在现实生活中:
人:张三、李四、王五…
水果:苹果、梨、香蕉…

在程序设计方法中:
抽象是对复杂世界的简单表示,抽象并不打算了解全部问题,而只强调感兴趣的信息,忽略了与主题无关的信息。

以学籍管理程序为例,通过对学生进行归纳出其中的共性,可以得到如下的抽象猫述
共同的属性(数据抽象部分):姓名、学号、成绩等
用C++语言的数据成员来表示:

1
2
3
char*name;	//姓名
int number; //学号
float score; //成绩

2. 封装

在现实世界中:

所谓封装就是把某个事物包围起来,外界是看不到的,甚至是不可知的。
例如:录音机、电视、照相机等。

在面向对象程序设计中:
封装是指把数据和实现操作的代码集中起来放在对象内部,并尽可能隐藏对象内部细节。

  • 对象好像是一个不透明的黑盒子,表示对象的属性和实现各个操作的代码都被封装在黑盒子里,从外面是看不见的,各个对象是相对独立的,互不干扰。
  • 对象至六次啊少量的接口,以便与外界联系。
  • C++对象中的成员函数名就是对象的对外接口,外界可以通过成员函数名进行调用函数实现某些操作。
  • 封装和抽象机制可以将对象的使用者与设计者分开,使用者不必知道对象行为实现的细节,只需要使用设计者提供的接口让对象去做。
  • 这样,大大降低了人们操作的复杂程度,还有利于数据安全。从而减轻了开发一软件系统的难度。

3. 继承

继承关系可以使子代继承父代的基本特性,又可以增加一些新的特性。

继承关系简化了人们对事物的认识和叙述,简化了工作程序。

若类之间具有继承关系,则它们之间具有下列几个特性:

  • 类间具有共享特性(包括数据和程序代码的共享)

  • 类间具有差别或新增部分(包括非共享的数据和程序代码)

  • 类间具有层次结构。

继承机制的作用:

  • 一是避免公用代码的重复开发,减少代码和数据冗余。
  • 二是通过增强一致性来减少模块间的接口和界面。

4. 多态

面向对象系统的多态性是指不同的对象收到相同的消息时,执行不同的操作。

C++支持的两种多态性:

  • 编译时的多态:通过函数重载和运算符重载来实现
  • 运行时的多态:通过虚函数来实现

类声明

类是对一群具有相同属性、表现相同行为的对象的抽象描述。

类中定义的数据(变量)称为数据成员
类中定义的函数(行为操作)称为成员函数

类声明的一般格式如下

1
2
3
4
5
6
7
8
9
10
11
12
class :: 类名
{
public:
公有数据成员;
公有成员函数;
protected:
保护数据成员;
保护成员函数;
private:
私有数据成员;
私有成员函数;
};

类成员的访问控制

私有段:由privater标记的段
段内的数据成员和成员函数称为私有成员,仅能由该类中的成员函数来访问,即仅可在成员函数定义中使用私有成员的名字

公有段:由public标记的段
段内的数据成员和成员函数称为公有成员,为该类提供与外部世界的接口界面,即可在类内也可在类外访问。

类中每一个成员都有访问控制属性,若没有明确指明,成员的访问控制方式缺省为private。

用一个类来描述日期,其形式如下:

1
2
3
4
5
6
7
8
9
10
class Data
{
public:
void setData(int u, int m, int d);
void showData();
private:
int year;
int mouth;
int day;
};

成员函数的声明通常采用以下两种方式:
(1) 将成员函数以普通函数的形式进行说明,这种成员函数在类外定义的一般形式是:

1
2
3
4
返回类型  类名∷成员函数名(参数表) 
{
// 函数体
}

(2) 将成员函数以内联函数的形式进行说明。
在C++中,可以用下面两种格式将成员函数声明为类的内联函数:
① 隐式声明 :直接将函数声明在类内部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Coord
{
public:
void setCoord(int a,int b)
{
x=a;
y=b;
}
int getx()
{
return x;
}
int gety()
{
retrun y;
}
private:
int x,y;
};

​ ② 显式声明:这种成员函数在类外定义。

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
inline 返回类型  类名::成员函数名(参数表)
{
// 函数体
}
// 例如上面的例子显示声明可以变成如下形式:
class Coord
{
public:
void setCoord(int,int);
int getx();
int gety();
private:
int x,y;
};
inline void Coord::setCoord(int a,int b)
{
x=a;
y=b;
}
inline int Coord::getx()
{
return x;
}
inline int Coord::gety()
{
return y;
}

对象的定义和使用

对象声明

一般格式: <类名> <对象名>

具有类类型的变量称为对象,对象称为类的实例(instance)。

对象的存储空间分配

定义了一个类的若干个对象后,系统会为每一个对象分配存储空间,对数据成员和函数成员的空间分配方式不同

对象的定义

定义格式与一般变量格式相同

对象中成员的访问

在类的外部可以通过类的对象进行访问,访问的一般形式是:
对象名.数据成员名对象名.成员函数名(参数表)
其中“.”叫做对象选择符,简称点运算符。

类成员的访问属性

类成员有三种访问属性:公有(public)、私有(private)和保护(protected)。

访问权限:public 可以被任意实体访问,protected 只允许子类(无论什么继承方式)及本类的成员函数访问,private 只允许本类的成员函数访问。三种继承方式分别是 public 继承,protect 继承,private 继承。

示例程序如下:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <String>

using namespace std;

// 基类
class student
{
public:
string name;
protected:
int age;
private:
char sex;

public:
void showStu()
{
cout << this->name << endl; // 1、在本类能够访问类的公有数据成员
cout << this->age << endl; // 1、在本类能够访问类的保护数据成员
cout << this->sex << endl; // 1、在本类能够访问类的私有数据成员
}
};

// 派生类 - public继承
class public_Sub : public student
{
public:
void show()
{
cout << this->name << endl; // 2、public继承,在派生类中能够访问基类的公有数据成员
cout << this->age << endl; // 2、public继承,在派生类中能够访问基类的保护数据成员
//cout << this->sex << endl; // error:2、在c类中不能访问基类的私有数据成员
}
};

// 派生类 - protected继承
class protected_Sub : protected student
{
public:
void show()
{
cout << this->name << endl; // 3、protected继承,在派生类中能够访问基类的公有数据成员
cout << this->age << endl; // 3、protected继承,在派生类中能够访问基类的保护数据成员
//cout << this->sex << endl; // error:3、在派生类中不能访问基类的私有数据成员
}
};

// 派生类 - private继承
class private_Sub : private student
{
public:
void show()
{
cout << this->name << endl; // 4、private继承,在派生类中能够访问基类的公有数据成员
cout << this->age << endl; // 4、private继承,在派生类中能够访问基类的保护数据成员 【即使是private继承】
//cout << this->sex << endl; // error:4、在派生类中不能访问基类的私有数据成员
}
};

int main()
{
student stu;
cout << stu.name << endl; // 5、在类外可以访问类的公有数据成员
//cout << stu.age << endl; // error,5、在类外不能访问类的保护数据成员
//cout << stu.sex << endl; // error,5、在类外不能访问类的私有数据成员

return 0;
}
  • 派生类内不管是 public、protected、private 继承,总是可以 public、protected 成员,基类中的 private 成员永远不能再派生类内直接访问,不论通过哪种方式。
  • 派生类对象仅当 public 派生时,对基类中的 public 成员有可访问/可修改的权限,其他都为不可访问/不可修改。

另外,继承方式会改变从基类继承的成员在派生类的访问权限:

1、public 继承不改变基类成员的访问权限;

2、protected 继承将基类中 public 成员变为子类的 protected 成员,其它成员的访问权限不变;

3、private 继承使得基类所有成员在子类中的访问权限变为 private。

C++ 中为什么要定义 public、protect、private 这三种访问权限?

有些我们需要给外面看的,也就是对外接口,那么就是 public。如果我们不想让别人知道内部的实现细节,那么就是 private。如果我们不想让别人知道,想让自己的孩子什么的知道(这里涉及到继承),那么就可以作为 protected。

类的作用域

所谓类的作用域就是指在类声明中的一对花括号所形成的作用域。

一个类的所有成员都在该类的作用域内,一个类的任何成员可以访问该类的其他成员。

而在类的外部,对该类的数据成员和成员函数的访问则要受到一定的限制,有时甚至是不允许的,这体现了类的封装功能

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
#include <iostream>
class myclass
{
public:
int i;
void init(int);
void show()
{
cout<<“i=”<<i<<endl;
} // 可以访问类中的数据成员i
};
void myclass::init(int si)
{
i=si;
} // 可以访问类中的数据成员i
int fun()
{
return i;
} // 非法,不能直接访问类中的i
void main()
{
myclass ob;
ob.init(5); // 给数据成员i赋初值5
ob.show();
i=8; // 非法,不能直接访问类中的i,可改写成ob.i=8
ob.show();
}

构造函数 & 析构函数

类的构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。

在实际应用中如果没有给类定义构造函数则编译系统自动地生成一个缺省的构造函数

下面的实例有助于更好地理解构造函数的概念:

实例

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}

void Line::setLength( double len )
{
length = len;
}

double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
Object is being created
Length of line : 6

带参数的构造函数

默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:

实例

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength( double len );
double getLength( void );
Line(double len); // 这是构造函数

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line( double len)
{
cout << "Object is being created, length = " << len << endl;
length = len;
}

void Line::setLength( double len )
{
length = len;
}

double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line(10.0);

// 获取默认设置的长度
cout << "Length of line : " << line.getLength() <<endl;
// 再次设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Object is being created, length = 10
Length of line : 10
Length of line : 6

使用初始化列表来初始化字段

使用初始化列表来初始化字段:

1
2
3
4
Line::Line( double len): length(len)
{
cout << "Object is being created, length = " << len << endl;
}

上面的语法等同于如下语法:

1
2
3
4
5
Line::Line( double len)
{
length = len;
cout << "Object is being created, length = " << len << endl;
}

假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:

1
2
3
4
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}

类的析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。当撤消对象时编译系统会自动地调用析构函数。

下面的实例有助于更好地理解析构函数的概念:

实例

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}

void Line::setLength( double len )
{
length = len;
}

double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Object is being created
Length of line : 6
Object is being deleted

静态成员

我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。

静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。

下面的实例有助于更好地理解静态成员数据的概念:

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
#include <iostream>

using namespace std;

class Box
{
public:
static int objectCount;
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次创建对象时增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
Box Box1(3.3, 1.2, 1.5); // 声明 box1
Box Box2(8.5, 6.0, 2.0); // 声明 box2

// 输出对象的总数
cout << "Total objects: " << Box::objectCount << endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Constructor called.
Constructor called.
Total objects: 2

友元

生活中你的家有客厅(public),有你的卧室(private)。客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去。
但是呢,你也可以允许隔壁老王进去。在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术。

友元的目的 就是让一个函数或者类访问另一个类中的私有成员。

友元的关键字为 friend

友元的三种实现:全局函数做友元、类做友元、成员函数做友元

1. 全局函数做友元

首先,我们要定义一个房屋类,公共成员变量为客厅,私有成员变量为卧室

然后,定义一个全局函数 laoWang(),用来访问Building类中的私有成员,也可以用引用传递或者最简单的值传递

最后定义一个测试函数test(),实现 laoWang() 这个全局函数做友元访问类的私有成员

但是,现在还不能实现全局函数访问类的私有成员!

关键代码

1
2
3
friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);

在Building类中声明友元函数,告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员

下面给出全局函数做友元访问类的私有成员的完整示例代码

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <string>

using namespace std;

// 房屋类
class Building
{
// 告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员
friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);

public:

Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}

string m_SittingRoom; // 客厅

private:

string m_BedRoom; // 卧室
};



//全局函数
void laoWang1(Building *building)
{
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;

cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}

void laoWang2(Building &building)
{
cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;

cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}

void laoWang3(Building building)
{
cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;

cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}

void test()
{
Building building;
laoWang1(&building);
laoWang2(building);
laoWang3(building);
}


int main()
{
test();
}

输出结果

1
2
3
4
5
6
隔壁老王 全局函数 正在访问:(地址传递) 客厅
隔壁老王 全局函数 正在访问:(地址传递) 卧室
隔壁老王 全局函数 正在访问:(引用传递) 客厅
隔壁老王 全局函数 正在访问:(引用传递) 卧室
隔壁老王 全局函数 正在访问:( 值传递 ) 客厅
隔壁老王 全局函数 正在访问:( 值传递 ) 卧室

2. 类做友元

首先,声明一个要访问的私有变量所属的Building类,防止在下面的好LaoWang类中,编译器不认识Building(当然也可以采取先定义Building类,再定义隔壁老王LaoWang类,这样就不用声明Building类了)

然后,定义一个隔壁老王LaoWang类,声明了一个Building类型的指针变量building。其中,成员函数采用另一种方式:类内声明,类外定义,可以简化类的内容(在全局函数做友元的示例中,采用在类内声明并定义成员函数的方式,两种方式均可)。接着给出Building的定义。

给出类外定义成员函数,需要注意的是,在类外定义,需要在成员函数加上所在类的作用域(类名::成员函数名()),以便于告诉编译器,该成员函数属于哪个类。

最后定义一个测试函数,实现类做友元访问其他类的私有成员

但是,现在还不能实现一个类访问另一个类的私有成员!

关键代码

1
friend class LaoWang;

在Building类中声明一个友元类,告诉编译器 LaoWang类是 Building类 的好朋友,可以访问Building对象的私有成员

下面给出类做友元实现一个类访问另一个类私有成员的完整示例代码

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <string>

using namespace std;

// 类作友元

class Building;
class LaoWang
{
public:

LaoWang();

void visit(); //参观函数 访问Building中的属性

Building * building;


private:


};

// 房屋类
class Building
{
// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
friend class LaoWang;
public:

Building();

string m_SittingRoom; // 客厅

private:

string m_BedRoom; // 卧室
};

// 类外定义成员函数

Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}

LaoWang::LaoWang()
{
// 创建建筑物对象
building = new Building;
}

void LaoWang::visit()
{
cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;

cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}

void test()
{
LaoWang lw;
lw.visit();
}

int main()
{
test();

return 0;
}

输出结果

1
2
隔壁老王LaoWang类正在访问:客厅
隔壁老王LaoWang类正在访问:卧室

3. 成员函数做友元

类似于类作友元,我们首先声明一个Building类,防止在下面的好LaoWang类中,编译器不认识Building

然后定义LaoWang类,同样采用成员函数在类内声明,类外定义的方式。其中定义两个访问函数

  • visit1(),可以 访问Building中的私有成员

  • visit2(),不可以 访问Building中的私有成员

    给出Building类的定义

    给出类外定义成员函数

    最后用一个测试函数实现成员函数做友元实现对另一个类私有成员的访问

    同样的,现在还没有声明友元,因此类中的成员函数还不能访问另一个类的私有成员

关键代码

1
friend void LaoWang::visit1();

在Building类中声明一个友元成员函数,告诉编译器 visit1()成员函数是 Building类 的好朋友,可以访问Building对象的私有成员

下面给出成员函数做友元实现一个类的成员函数访问另一个类私有成员的完整示例代码

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <string>

using namespace std;

class Building;

class LaoWang
{
public:

LaoWang();
void visit1(); //让visit1()函数 可以 访问Building中的私有成员
void visit2(); //让visit2()函数 不可以 访问Building中的私有成员

Building *building;

private:


};

class Building
{
// 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
friend void LaoWang::visit1();

public:
Building();

string m_SittingRoom; //客厅
private:

string m_BedRoom; //卧室
};


LaoWang::LaoWang()
{
building = new Building;
}

void LaoWang::visit1()
{
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}

void LaoWang::visit2()
{
cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl; //错误!私有属性不可访问
}

Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}

void test()
{
LaoWang lw;

lw.visit1();

lw.visit2();
}

int main()
{
test();

return 0;
}

输出结果

1
2
3
隔壁老王LaoWang类中的visit1()函数正在访问:客厅
隔壁老王LaoWang类中的visit1()函数正在访问:卧室
隔壁老王LaoWang类中的visit2()函数正在访问:客厅

类的组合

组合的概念

  • 类中的成员是另一个类的对象。
  • 可以在已有抽象的基础上实现更复杂的抽象。

类组合的构造函数设计

  • 原则:不仅要负责对本类中的基本类型成员数据初始化,也要对对象成员初始化。
  • 声明形式:
1
2
3
4
类名::类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),......
{
//函数体其他语句
}

初始化列表的其他用途

基本数据类型的数据成员也可用初始化列表做初始化。

1
2
3
4
5
6
7
8
class C
{
public:
C(int i):number(i)
{……}
private:
int number;
};

等价于

1
2
3
4
5
6
7
8
9
10
11
class C
{
public:
C(int i)
{
number = i;
……
}
private:
int number;
};

构造组合类对象时的初始化次序

首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序。

成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造,与初始化参数列表顺序无关。

初始化列表中未出现的成员对象,调用用默认构造函数(即无形参的)初始化

处理完初始化列表之后,再执行构造函数的函数体。

类组合程序举例

例4-4 类的组合,线段(Line)类

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <cmath>
using namespace std;
class Point
{ //Point类定义
public:
Point(int xx = 0, int yy = 0)
{
x = xx;
y = yy;
}
Point(Point &p);
int getX() { return x; }
int getY() { return y; }
private:
int x, y;
};

Point::Point(Point &p)
{ //复制构造函数的实现
x = p.x;
y = p.y;
cout << "Calling the copy constructor of Point" << endl;
}

//类的组合
class Line
{ //Line类的定义
public: //外部接口
Line(Point xp1, Point xp2);
Line(Line &l);
double getLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};

//组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2)
{
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
Line::Line(Line &l) : p1(l.p1), p2(l.p2)
{//组合类的复制构造函数
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}

//主函数
int main()
{
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
Line line2(line); //利用复制构造函数建立一个新对象
cout << "The length of the line is: ";
cout << line.getLen() << endl;
cout << "The length of the line2 is: ";
cout << line2.getLen() << endl;
return 0;
}

运行结果

1
2
The length of the line is: 5
The length of the line2 is: 5