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

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

1. 从结构体到类

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

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

cpp 复制代码
// C 风格:数据和操作分离
struct Student {
    char name[50];
    int age;
    double score;
};
// 外部可以直接修改:stu.score = -100;  // 不合理但合法

// C++ 风格:封装,数据私有,通过公有接口访问
class Student {
private:
    std::string name;
    int age;
    double score;
public:
    void set_score(double s) { if (s >= 0) score = s; }
    double get_score() const { return score; }
};

2. class vs struct

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

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

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

访问级别 本类内部 派生类 外部代码
public
protected
private
cpp 复制代码
class Demo {
private:
    int a;        // 只有本类能访问
protected:
    int b;        // 本类和派生类能访问
public:
    int c;        // 谁都能访问
};

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

4. 类的构成

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

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

头文件:类定义

cpp 复制代码
// student.h
#ifndef STUDENT_H_
#define STUDENT_H_

#include <string>

class Student {
private:
    std::string name;
    int age;
    double score;

public:
    // 构造函数与析构函数
    Student(const std::string& n, int a, double s);
    ~Student();

    // 成员函数
    void show() const;
    std::string get_name() const { return name; }  // 内联函数
    int get_age() const { return age; }
    void set_score(double s);
    double get_score() const;
};

#endif

源文件:成员函数实现

cpp 复制代码
// student.cpp
#include <iostream>
#include "student.h"

// 构造函数:使用成员初始化列表
Student::Student(const std::string& n, int a, double s)
    : name(n), age(a), score(s) {
    std::cout << "构造: " << name << std::endl;
}

// 析构函数
Student::~Student() {
    std::cout << "析构: " << name << std::endl;
}

// const 成员函数:承诺不修改对象
void Student::show() const {
    std::cout << name << ", " << age << "岁, 成绩: " << score << std::endl;
}

void Student::set_score(double s) {
    score = s;
}

double Student::get_score() const {
    return score;
}

主程序

cpp 复制代码
#include <iostream>
#include "student.h"

int main() {
    Student stu("Alice", 20, 85.5);
    stu.show();
    stu.set_score(92.0);
    std::cout << "最新成绩: " << stu.get_score() << std::endl;
    
    // stu.score = 100;  // ❌ 错误!score 是 private
    return 0;
}

编译:

bash 复制代码
g++ main.cpp student.cpp -o class_demo

5. 内联成员函数

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

cpp 复制代码
class Student {
public:
    // 类内定义 → 自动成为内联函数
    std::string get_name() const { return name; }
};

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

cpp 复制代码
inline std::string Student::get_name() const {
    return name;
}

6. 多个对象互不干扰

cpp 复制代码
Student stu1("Alice", 20, 85.5);
Student stu2("Bob", 22, 78.0);
Student stu3("Charlie", 19, 91.5);

stu1.get_name();  // "Alice"
stu2.get_name();  // "Bob"  ← 每个对象独立存储自己的 name

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

7. 总结

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

互动测验(选择题)

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

cpp 复制代码
class Student { int age; };

age 默认是什么?

A. public

B. private

C. protected

D. 取决于编译器

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

第 2 题:封装体现的是?

cpp 复制代码
class Student {
private:
    double score;
public:
    void set_score(double s) { score = s; }
    double get_score() const { return score; }
};

A. 继承

B. 封装(数据隐藏 + 公有接口)

C. 多态

D. 函数重载

答案:B

第 3 题:构造函数的特点

A. 有返回值,可以声明为 void

B. 函数名和类名相同,没有返回值,创建对象时自动调用

C. 需要手动调用

D. 只能有一个构造函数

答案:B

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

A. 对象销毁时自动调用

B. 函数名是 ~类名

C. 可以有参数

D. 用于释放资源

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

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

cpp 复制代码
void show() const;

A. 修饰返回值

B. 修饰函数参数

C. 承诺该函数不会修改对象的数据成员

D. 让函数运行更快

答案:C


练习题

习题 1:创建自己的类

定义一个 Rectangle 类:

cpp 复制代码
class Rectangle {
private:
    double width;   // 宽
    double height;  // 高
public:
    // 构造函数
    Rectangle(double w, double h);
    ~Rectangle();
    
    // 计算面积
    double area() const;
    // 计算周长
    double perimeter() const;
    
    // getter/setter
    double get_width() const;
    double get_height() const;
    void set_width(double w);
    void set_height(double h);
    
    // 显示信息
    void show() const;
};

要求:

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

习题 2:分析题

cpp 复制代码
struct Point {
    int x;
    int y;
};

class Circle {
private:
    Point center;
    double radius;
};

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

习题 3:分析题

以下代码有什么问题?

cpp 复制代码
class Logger {
    std::string prefix;
public:
    void log(const std::string& msg) {
        std::cout << "[" << prefix << "] " << msg << std::endl;
    }
};

int main() {
    Logger log;
    log.log("hello");
    return 0;
}

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

相关推荐
杨了个杨898220 小时前
Keepalived + Nginx + HAProxy 高可用架构部署实战案例
java·nginx·架构
kaikaile199520 小时前
数字全息图处理系统(C# 实现)
开发语言·c#
秋921 小时前
Go语言(Golang)开发工程师全景解析:岗位职责·语言优势与使用场景·各城市薪资·发展前景·高考志愿填报(2026版)
开发语言·golang·高考
huangdong_1 天前
1688商品图片采集技术解析:登录态处理与SKU图自动分类
开发语言
马士兵教育1 天前
Java还有前景吗?Java+AI大模型学习路线及项目?
java·人工智能·python·学习·机器学习
搬砖魁首1 天前
基础能力系列 - 多线程2 - 条件变量
c++·rust·条件变量·原子类型·线程同步互斥
chase_my_dream1 天前
C++ + SLAM 高频面试问题整理
开发语言·c++·面试
snow@li1 天前
Java:理解 Gradle / 后端项目的管家 / 打包SpringBoot 应用 / 完成编译、下载依赖、运行测试、打包 JAR/WAR / 速查表
java
牛油果子哥q1 天前
【C++ STL string 】C++ STL string 终极精讲:底层原理、内存机制、全套API、深浅拷贝、易错坑点与工程实战规范
数据库·c++
Cloud_Shy6181 天前
解读《Effective Python 3rd Edition》:从练气到老魔(第五章 Item 30 - 32)
开发语言·人工智能·笔记·python·学习方法