Lazy loaded image
C++ 中的类与对象
字数 8394阅读时长 21 分钟
2025-12-8
2025-12-10
password
comment
type
status
date
slug
summary
tags
category
icon

C++ 中的类与对象

C++ 在C语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,是一种用户自定义的数据类型,它是一种封装了数据和函数的组合。类中的数据称为成员变量,函数称为成员函数。类可以被看作是一种模板,可以用来创建具有相同属性和行为的多个对象。

C++ 中类的定义

定义一个类需要使用关键字 class,然后指定类的名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。
定义一个类,本质上是定义一个数据类型的蓝图,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
notion image
以下实例我们使用关键字 class 定义 Box 数据类型,包含了三个成员变量 lengthbreadthheight
关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。我们也可以指定类的成员为 private 或 protected,这个我们稍后会进行讲解。

C++ 中对象的定义

类提供了对象的蓝图,所以基本上,对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。下面的语句声明了类 Box 的两个对象:
对象 Box1Box2 都有它们各自的数据成员。

数据成员的访问

类的对象的公共数据成员可以使用直接成员访问运算符 . 来访问。
notion image
为了更好地理解这些概念,让我们尝试一下下面的实例:
当上面的代码被编译和执行时,它会产生下列结果:
需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (.) 来直接访问。我们将在后续的教程中学习如何访问私有成员和受保护的成员。

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,具有多个字段 XYZ 等需要进行初始化,同理地,我们可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:

类的析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
下面的实例有助于更好地理解析构函数的概念:
当上面的代码被编译和执行时,它会产生下列结果:

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 指针。
下面的实例有助于更好地理解静态成员函数的概念:
当上面的代码被编译和执行时,它会产生下列结果:
上一篇
C语言中的函数指针与回调函数(C14)
下一篇
C++ 中的继承

评论
Loading...