01C++ 类定义与访问控制(封装)
2026/5/1 17:48:22 网站建设 项目流程

C++ 类定义与访问控制(封装)

1. 从结构体到类

在 C 语言中,我们用struct把相关数据组合在一起。但问题是——任何人都可以直接修改结构体的成员,无法控制数据的合法性。

C++ 的class解决了这个问题:把数据和操作数据的方法捆绑在一起,并控制外部对数据的访问权限

// C 风格:数据和操作分离structStudent{charname[50];intage;doublescore;};// 外部可以直接修改:stu.score = -100; // 不合理但合法// C++ 风格:封装,数据私有,通过公有接口访问classStudent{private:std::string name;intage;doublescore;public:voidset_score(doubles){if(s>=0)score=s;}doubleget_score()const{returnscore;}};

2. class vs struct

对比classstruct
默认访问控制privatepublic
能否定义成员函数
能否有构造函数/析构函数
能否继承能 (默认private继承)能 (默认public继承)
使用惯例有封装逻辑的复杂类型单纯的数据聚合

实际上 C++ 中 struct 几乎和 class 一样,唯一的区别是默认访问权限。

3. 访问控制:public / private / protected

访问级别本类内部派生类外部代码
public
protected
private
classDemo{private:inta;// 只有本类能访问protected:intb;// 本类和派生类能访问public:intc;// 谁都能访问};

封装的原则:成员变量尽量 private,通过 public 的 getter/setter 提供受控访问。

4. 类的构成

一个完整的类通常放在两个文件中:

student.h ← 头文件:类定义(成员变量 + 函数原型) student.cpp ← 源文件:成员函数实现 main.cpp ← 使用类

头文件:类定义

// student.h#ifndefSTUDENT_H_#defineSTUDENT_H_#include<string>classStudent{private:std::string name;intage;doublescore;public:// 构造函数与析构函数Student(conststd::string&n,inta,doubles);~Student();// 成员函数voidshow()const;std::stringget_name()const{returnname;}// 内联函数intget_age()const{returnage;}voidset_score(doubles);doubleget_score()const;};#endif

源文件:成员函数实现

// student.cpp#include<iostream>#include"student.h"// 构造函数:使用成员初始化列表Student::Student(conststd::string&n,inta,doubles):name(n),age(a),score(s){std::cout<<"构造: "<<name<<std::endl;}// 析构函数Student::~Student(){std::cout<<"析构: "<<name<<std::endl;}// const 成员函数:承诺不修改对象voidStudent::show()const{std::cout<<name<<", "<<age<<"岁, 成绩: "<<score<<std::endl;}voidStudent::set_score(doubles){score=s;}doubleStudent::get_score()const{returnscore;}

主程序

#include<iostream>#include"student.h"intmain(){Studentstu("Alice",20,85.5);stu.show();stu.set_score(92.0);std::cout<<"最新成绩: "<<stu.get_score()<<std::endl;// stu.score = 100; // ❌ 错误!score 是 privatereturn0;}

编译:

g++ main.cpp student.cpp-oclass_demo

5. 内联成员函数

在类定义中直接实现的函数自动成为内联函数

classStudent{public:// 类内定义 → 自动成为内联函数std::stringget_name()const{returnname;}};

内联函数在编译期将函数调用替换为函数体代码,避免函数调用的开销。适合实现简单的 getter/setter。也可以显式用inline关键字:

inlinestd::stringStudent::get_name()const{returnname;}

6. 多个对象互不干扰

Studentstu1("Alice",20,85.5);Studentstu2("Bob",22,78.0);Studentstu3("Charlie",19,91.5);stu1.get_name();// "Alice"stu2.get_name();// "Bob" ← 每个对象独立存储自己的 name

每个对象有自己独立的成员变量副本,互不干扰。

7. 总结

知识点要点
封装private 数据 + public 接口 = 受控访问
class vs structclass 默认 private,struct 默认 public
构造函数同名、无返回值、创建时自动调用
析构函数~类名、无参数无返回值、销毁时自动调用
const 成员函数尾部加const,不修改对象
内联函数类内定义自动内联,适合 getter/setter
多文件组织.h 放定义,.cpp 放实现

互动测验(选择题)

第 1 题:class 的默认访问控制

classStudent{intage;};

age默认是什么?

A. public
B. private
C. protected
D. 取决于编译器

答案:B。class 默认 private,struct 默认 public,这是两者唯一区别。

第 2 题:封装体现的是?

classStudent{private:doublescore;public:voidset_score(doubles){score=s;}doubleget_score()const{returnscore;}};

A. 继承
B. 封装(数据隐藏 + 公有接口)
C. 多态
D. 函数重载

答案:B

第 3 题:构造函数的特点

A. 有返回值,可以声明为 void
B. 函数名和类名相同,没有返回值,创建对象时自动调用
C. 需要手动调用
D. 只能有一个构造函数

答案:B

第 4 题:关于析构函数,错误的是?

A. 对象销毁时自动调用
B. 函数名是~类名
C. 可以有参数
D. 用于释放资源

答案:C。析构函数无参数。

第 5 题:const 成员函数的作用

voidshow()const;

A. 修饰返回值
B. 修饰函数参数
C. 承诺该函数不会修改对象的数据成员
D. 让函数运行更快

答案:C


练习题

习题 1:创建自己的类

定义一个Rectangle类:

classRectangle{private:doublewidth;// 宽doubleheight;// 高public:// 构造函数Rectangle(doublew,doubleh);~Rectangle();// 计算面积doublearea()const;// 计算周长doubleperimeter()const;// getter/setterdoubleget_width()const;doubleget_height()const;voidset_width(doublew);voidset_height(doubleh);// 显示信息voidshow()const;};

要求:

  • 使用成员初始化列表初始化 width 和 height
  • 在 setter 中检查宽高必须为正数
  • 创建多个 Rectangle 对象测试

习题 2:分析题

structPoint{intx;inty;};classCircle{private:Point center;doubleradius;};

问:PointxyCircle外部可以直接访问吗?Circlecenterradius呢?为什么?

习题 3:分析题

以下代码有什么问题?

classLogger{std::string prefix;public:voidlog(conststd::string&msg){std::cout<<"["<<prefix<<"] "<<msg<<std::endl;}};intmain(){Logger log;log.log("hello");return0;}

能编译通过吗?prefix的值是什么?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询