password
comment
type
status
date
slug
summary
tags
category
icon
C++ 中的类与对象
C++ 在C语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,是一种用户自定义的数据类型,它是一种封装了数据和函数的组合。类中的数据称为成员变量,函数称为成员函数。类可以被看作是一种模板,可以用来创建具有相同属性和行为的多个对象。
C++ 中类的定义
定义一个类需要使用关键字
class,然后指定类的名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。定义一个类,本质上是定义一个数据类型的蓝图,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。

以下实例我们使用关键字
class 定义 Box 数据类型,包含了三个成员变量 length、breadth 和 height:关键字
public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。我们也可以指定类的成员为 private 或 protected,这个我们稍后会进行讲解。C++ 中对象的定义
类提供了对象的蓝图,所以基本上,对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。下面的语句声明了类
Box 的两个对象:对象
Box1 和 Box2 都有它们各自的数据成员。数据成员的访问
类的对象的公共数据成员可以使用直接成员访问运算符
. 来访问。
为了更好地理解这些概念,让我们尝试一下下面的实例:
当上面的代码被编译和执行时,它会产生下列结果:
需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (
.) 来直接访问。我们将在后续的教程中学习如何访问私有成员和受保护的成员。C++ 中的类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
让我们看看之前定义的类
Box,现在我们要使用成员函数来访问类的成员,而不是直接访问这些类的成员:成员函数可以定义在类定义内部,或者单独使用范围解析运算符
:: 来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。所以您可以按照如下方式定义 getVolume() 函数:我们也可以在类的外部使用范围解析运算符
:: 定义该函数,如下所示:在这里,需要强调一点,在
:: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据,如下所示:让我们使用上面提到的概念来设置和获取类中不同的成员的值:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中类的访问修饰符
在 C++ 面向对象编程(OOP)中,数据封装(Encapsulation) 是核心概念之一。简单来说,就是把数据"藏"起来,只开放必要的接口给外界使用,以保证数据的安全。
为了控制谁能看到这些数据,C++ 提供了三个关键字,称为访问修饰符:
public(公有)
private(私有)
protected(受保护)
为了方便理解,我们可以把一个类(
Class)想象成你的家:修饰符 | 现实类比 | 谁可以访问? | 典型用途 |
public | 客厅/大门 | 所有人(类内部、子类、外部代码) | 对外提供的接口函数(API)。 |
protected | 卧室 | 你和你的孩子(类内部、子类) | 仅限家族内部(继承体系)使用的数据。 |
private | 保险箱 | 只有你自己(仅限本类内部) | 核心数据、不想被随意修改的变量。 |
注意:如果不写任何修饰符,C++ 类成员默认是private(私有) 的。
公有(public)成员
公有成员在程序中任何地方都能访问,无需通过成员函数读写,示例如下:
当上面的代码被编译和执行时,它会产生下列结果:
私有(private)成员
私有成员对类外完全封闭,外部代码无法读取、修改或调用,派生类同样无权直接访问。只有类自身的成员函数与被授予友元权限的实体能够操作这些内容。
类中若未使用任何访问说明符,成员默认为私有。如下例所示,
width 自动落入私有区域,表明未明确标注的成员均按私有处理:实际操作中,我们一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数,如下所示:
当上面的代码被编译和执行时,它会产生下列结果:
受保护(protected)成员
protected 的存在主要是为了继承。- 如果没有继承,它和
private一样(外人不可见)。
- 如果有继承,子类(派生类)可以访问父类的
protected成员,但不能访问private成员。
在下一个章节中,我们将学习到派生类和继承的知识。现在我们可以看到下面的实例中,我们从父类
Box 派生了一个子类 smallBox。下面的实例与前面的实例类似,在这里
width 成员可被派生类 smallBox 的任何成员函数访问。当上面的代码被编译和执行时,它会产生下列结果:
继承中的访问权限变化
当你定义一个子类时(如
class B: public A),冒号后面的 public 也是一种访问修饰符,它决定了父类的成员在子类中"表现"成什么样。这看起来很复杂,但只需要记住这个降级原则:
继承方式决定了父类成员在子类中的最高权限。如果继承方式比成员原本的权限更严格,成员的权限就会被"降级"到继承方式的级别。
权限变化表
父类成员原权限 | public 继承 (最常用) | protected 继承 | private 继承 |
public | 保持 public | 降级为 protected | 降级为 private |
protected | 保持 protected | 保持 protected | 降级为 private |
private | 不可访问 | 不可访问 | 不可访问 |
注:不可访问指子类代码无法直接使用该变量,但变量依然存在于内存中。
综合演示代码
为了清晰展示,我们将变量重命名为
pub_var (公有), pro_var (受保护), pri_var (私有)。总结
Public: 谁都能用。做接口(API)用。
Private: 只有自己能用。存数据用(默认安全选项)。
Protected: 只有家族(继承链)能用。给子类留后门用。
继承:
Public继承:父类的属性保持不变(最常用)。
Private/Protected继承:会收紧父类成员的权限,通常用于特殊的实现场景。
C++ 中类的构造函数与析构函数
类的构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回
void。构造函数可用于为某些成员变量设置初始值。下面的实例有助于更好地理解构造函数的概念:
当上面的代码被编译和执行时,它会产生下列结果:
带参数的构造函数
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:
当上面的代码被编译和执行时,它会产生下列结果:
使用初始化列表来初始化字段
使用初始化列表来初始化字段:
上面的语法等同于如下语法:
假设有一个类
C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,我们可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:类的析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(
~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。下面的实例有助于更好地理解析构函数的概念:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中的拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
- 通过使用另一个同类型的对象来初始化新创建的对象。
- 复制对象把它作为参数传递给函数。
- 复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
在这里,
obj 是一个对象引用,该对象是用于初始化另一个对象的。当上面的代码被编译和执行时,它会产生下列结果:
下面的实例对上面的实例稍作修改,通过使用已有的同类型的对象来初始化新创建的对象:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中的友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(
private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字
friend,如下所示:声明类
ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:请看下面的程序:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中的内联函数
C++ 中的内联函数通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字
inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。在类定义中的定义的函数都是内联函数,即使没有使用
inline 说明符。下面是一个实例,使用内联函数来返回两个数中的最大值:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中的 this 指针
在 C++ 中,
this 指针是一个特殊的指针,它指向当前对象的实例。在 C++ 中,每一个对象都能通过
this 指针来访问自己的地址。this是一个隐藏的指针,可以在类的成员函数中使用,它可以用来指向调用对象。当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为
this 指针。友元函数没有
this 指针,因为友元不是类的成员,只有成员函数才有 this 指针。下面的实例有助于更好地理解
this 指针的概念:以上代码执行输出结果为:
实例解析:
- 以上实例中,我们定义了一个名为
MyClass的类,它有一个私有成员变量value。
- 类中的
setValue()函数用于设置value的值,而printValue()函数用于打印value的值。
- 在
setValue()函数中,我们使用this指针来引用当前对象的成员变量value,并将传入的值赋给它,这样可以明确地告诉编译器我们想要访问当前对象的成员变量,而不是函数参数或局部变量。
- 在
printValue()函数中,我们同样使用this指针来引用当前对象的成员变量value,并将其打印出来。
- 在
main()函数中,我们创建了一个MyClass的对象obj,然后使用setValue()函数设置value的值为42,并通过printValue()函数打印出来。
- 通过使用
this指针,我们可以在成员函数中访问当前对象的成员变量,即使它们与函数参数或局部变量同名,这样可以避免命名冲突,并确保我们访问的是正确的变量。
更多实例
以下实例用于比较长方体的体积:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中指向类的指针
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符
->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。在 C++ 中,指向类的指针指向一个类的对象,与普通的指针相似,指向类的指针可以用于访问对象的成员变量和成员函数。
声明和初始化指向类的指针
当上面的代码被编译和执行时,它会产生下列结果:
动态分配内存
指向类的指针还可以用于动态分配内存,创建类的对象:
当上面的代码被编译和执行时,它会产生下列结果:
指向类的指针作为函数参数
指向类的指针可以作为函数参数传递:
实例
下面的实例有助于更好地理解指向类的指针的概念:
当上面的代码被编译和执行时,它会产生下列结果:
C++ 中类的静态成员
我们可以使用
static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符
:: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。下面的实例有助于更好地理解静态成员数据的概念:
当上面的代码被编译和执行时,它会产生下列结果:
静态成员函数
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符
:: 就可以访问。静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的
this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。静态成员函数与普通成员函数的区别:
- 静态成员函数没有
this指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
- 普通成员函数有
this指针,可以访问类中的任意成员;而静态成员函数没有this指针。
下面的实例有助于更好地理解静态成员函数的概念:
当上面的代码被编译和执行时,它会产生下列结果:
- 作者:计算机类2507班
- 链接:https://learning.lcyteam.me//article/cpp-classes-objects
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。









