首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《C++ 程序设计》第 5 章 - 数据的共享与保护

《C++ 程序设计》第 5 章 - 数据的共享与保护

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:34:14
发布2026-01-21 12:34:14
70
举报

 引言

        在 C++ 程序设计中,数据的共享与保护是确保程序正确性、安全性和高效性的核心环节。本章将深入探讨标识符的作用域、对象的生存期、类的静态成员、友元机制、共享数据的保护策略、多文件结构及编译预处理等关键知识点。通过理论讲解与实战代码相结合的方式,帮助读者掌握数据共享与保护的精髓。

本章知识思维导图

5.1 标识符的作用域与可见性

        在 C++ 中,标识符(变量、函数、类等)的作用域指的是标识符有效存在的区域,而可见性则指标识符在程序的哪些部分可以被访问。

5.1.1 作用域

C++ 中常见的作用域类型包括:

  • 函数作用域:仅在函数内部有效(如函数参数)
  • 块作用域:在 {} 包围的代码块内有效
  • 类作用域:类的成员在类的范围内有效
  • 文件作用域:在整个源文件中有效(如全局变量)
  • 命名空间作用域:在命名空间内有效
作用域示例代码
代码语言:javascript
复制
#include <iostream>
using namespace std;

// 文件作用域:全局变量,在整个文件中有效
int global_var = 100; 

void test_function(int param) { // param是函数作用域
    // 块作用域1
    int local_var = 200;
    cout << "函数内局部变量: " << local_var << endl;
    
    // 块作用域2(嵌套块)
    {
        int nested_var = 300;
        // 可以访问外部块和全局变量
        cout << "嵌套块变量: " << nested_var << endl;
        cout << "访问外部块变量: " << local_var << endl;
        cout << "访问全局变量: " << global_var << endl;
    }
    
    // 错误:无法访问嵌套块内的变量
    // cout << nested_var << endl; 
}

int main() {
    test_function(5); // 5是函数作用域的参数
    cout << "全局变量: " << global_var << endl;
    
    // 错误:无法访问test_function内的局部变量
    // cout << local_var << endl;
    
    return 0;
}
5.1.2 可见性

        可见性遵循 "内层可见外层,外层不可见内层" 的规则,当内层与外层有同名标识符时,内层标识符会隐藏外层标识符。

可见性与标识符隐藏示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

int var = 10; // 全局变量

void func() {
    int var = 20; // 局部变量,隐藏全局变量
    cout << "函数内的var: " << var << endl; // 输出20
    
    // 使用::访问被隐藏的全局变量
    cout << "全局的var: " << ::var << endl; // 输出10
    
    {
        int var = 30; // 嵌套块变量,隐藏外层的局部变量
        cout << "嵌套块的var: " << var << endl; // 输出30
        cout << "外层局部var: " << ::var << endl; // 这里::仍表示全局
    }
}

int main() {
    func();
    cout << "main中的var: " << var << endl; // 输出10
    return 0;
}

5.2 对象的生存期

        对象的生存期指的是对象从创建到销毁的这段时间,根据生存期不同可分为静态生存期和动态生存期。

5.2.1 静态生存期

具有静态生存期的对象在程序运行期间始终存在,包括:

  • 全局对象
  • static修饰的局部对象
  • 静态成员对象
静态生存期示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

// 全局对象:静态生存期
int global_obj = 10;

void func() {
    // 静态局部对象:只初始化一次,程序结束时销毁
    static int static_local = 0;
    int dynamic_local = 0; // 动态生存期
    
    static_local++;
    dynamic_local++;
    
    cout << "静态局部变量: " << static_local << endl;
    cout << "动态局部变量: " << dynamic_local << endl;
}

int main() {
    cout << "第一次调用func():\n";
    func();
    
    cout << "\n第二次调用func():\n";
    func();
    
    cout << "\n第三次调用func():\n";
    func();
    
    return 0;
}

运行结果:

5.2.2 动态生存期

动态生存期的对象在程序执行到创建点时被创建,离开作用域时被销毁,主要是局部非静态对象。

动态生存期与静态生存期对比流程图
关键对比说明:

特征

动态生存期

静态生存期

存储位置

堆内存

静态存储区

创建方式

new操作符显式创建

自动初始化(全局/静态声明)

销毁时机

手动delete或智能指针离开作用域

程序结束时自动销毁

生存期控制

程序员完全控制

编译器管理

初始化时机

运行时动态创建

main()前或首次访问时(局部静态)

内存管理

需手动管理,风险:内存泄漏/悬空指针

自动管理,风险:初始化顺序问题

典型用例

运行时决定大小的数据结构

全局配置、单例模式、缓存机制

流程详解:

动态生存期

  1. 通过new在堆上分配内存
  2. 调用构造函数初始化对象
  3. 对象使用阶段
  4. 销毁方式二选一:
    • 手动delete显式销毁
    • 智能指针自动销毁(离开作用域时)
  5. 调用析构函数释放资源
  6. 释放堆内存

静态生存期

  1. 程序启动时初始化:
    • 全局/静态成员:在main()执行前初始化
    • 局部静态:首次执行到声明处时初始化
  2. 对象在整个程序运行期可用
  3. 程序终止时:
    • 按初始化逆序调用析构函数
    • 释放静态存储区内存

最佳实践提示:优先使用智能指针(unique_ptr/shared_ptr)管理动态对象,避免内存泄漏。静态对象需注意初始化顺序问题(跨编译单元时可能未初始化)。

5.3 类的静态成员

        当类的所有对象需要共享同一数据时,可以使用静态成员。静态成员不属于某个特定对象,而是属于整个类。

5.3.1 静态数据成员

静态数据成员的特点:

  • 在类内声明,类外初始化
  • 所有对象共享同一数据
  • 可以通过类名直接访问(类名::成员名)
静态数据成员示例
代码语言:javascript
复制
#include <iostream>
#include <string>  // 注意添加string头文件
using namespace std;

class Student {
private:
    string name;
    int id;
    // 静态数据成员:统计学生总数
    static int count; 
    
public:
    // 默认构造函数
    Student() : name(""), id(0) {
        // 注意:默认构造函数创建的对象是否计入总数,根据需求决定
        // 如果不想计入,可以注释掉下面这行
        count++;
    }
    
    // 带参数的构造函数
    Student(string n, int i) : name(n), id(i) {
        count++; // 每创建一个对象,总数加1
    }
    
    // 析构函数
    ~Student() {
        count--; // 每销毁一个对象,总数减1
    }
    
    // 获取学生总数
    int getCount() {
        return count;
    }
    
    void display() {
        cout << "姓名: " << name << ", 学号: " << id << endl;
    }
};

// 静态数据成员初始化(类外)
int Student::count = 0;

int main() {
    // 现在可以创建无参数的Student对象了
    cout << "初始学生总数: " << Student().getCount() << endl;
    
    Student s1("张三", 1001);
    cout << "创建s1后总数: " << s1.getCount() << endl;
    s1.display();
    
    Student s2("李四", 1002);
    cout << "创建s2后总数: " << s2.getCount() << endl;
    s2.display();
    
    {
        Student s3("王五", 1003);
        cout << "创建s3后总数: " << s3.getCount() << endl;
        s3.display();
    } // s3在这里销毁
    
    cout << "销毁s3后总数: " << s2.getCount() << endl;
    
    return 0;
}

运行结果:

5.3.2 静态函数成员

静态函数成员的特点:

  • 没有 this 指针,不能访问非静态成员
  • 可以直接通过类名调用
  • 主要用于操作静态数据成员
静态函数成员示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

class MathUtil {
private:
    // 静态常量:圆周率
    static const double PI; 
    
public:
    // 静态函数成员:计算圆的面积
    static double calculateCircleArea(double radius) {
        // 静态函数可以访问静态数据成员
        return PI * radius * radius;
    }
    
    // 静态函数成员:计算圆的周长
    static double calculateCircleCircumference(double radius) {
        return 2 * PI * radius;
    }
};

// 初始化静态常量
const double MathUtil::PI = 3.1415926535;

int main() {
    double radius = 5.0;
    
    // 通过类名直接调用静态函数,无需创建对象
    double area = MathUtil::calculateCircleArea(radius);
    double circumference = MathUtil::calculateCircleCircumference(radius);
    
    cout << "半径为" << radius << "的圆:" << endl;
    cout << "面积: " << area << endl;
    cout << "周长: " << circumference << endl;
    
    return 0;
}

5.4 类的友元

        友元机制允许类外的函数或其他类访问该类的私有成员,提供了数据共享的灵活性,但会在一定程度上破坏封装性。

5.4.1 友元函数

友元函数不是类的成员函数,但可以访问类的私有成员。

友元函数示例
代码语言:javascript
复制
#include <iostream>
#include <cmath>
using namespace std;

class Point {
private:
    double x; // x坐标
    double y; // y坐标
    
public:
    // 构造函数
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    
    // 声明友元函数:计算两点距离
    friend double distanceBetweenPoints(const Point& p1, const Point& p2);
    
    void display() const {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

// 实现友元函数
double distanceBetweenPoints(const Point& p1, const Point& p2) {
    // 友元函数可以直接访问私有成员x和y
    double dx = p1.x - p2.x;
    double dy = p1.y - p2.y;
    return sqrt(dx*dx + dy*dy);
}

int main() {
    Point p1(1, 2);
    Point p2(4, 6);
    
    cout << "点p1: ";
    p1.display();
    cout << "点p2: ";
    p2.display();
    
    // 调用友元函数
    double dist = distanceBetweenPoints(p1, p2);
    cout << "两点之间的距离: " << dist << endl;
    
    return 0;
}
5.4.2 友元类

当一个类被声明为另一个类的友元时,这个类的所有成员函数都可以访问另一个类的私有成员。

友元类示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

// 前向声明
class Date;

class Person {
private:
    string name;
    int age;
    
public:
    Person(string n, int a) : name(n), age(a) {}
    
    // 声明Date类为友元类
    friend class Date;
};

class Date {
private:
    int year;
    int month;
    int day;
    
public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {}
    
    // 友元类的成员函数可以访问Person的私有成员
    void displayPersonInfo(Person& p) {
        cout << "姓名: " << p.name << ", 年龄: " << p.age << endl;
        cout << "当前日期: " << year << "-" << month << "-" << day << endl;
    }
};

int main() {
    Person p("张三", 25);
    Date d(2023, 10, 1);
    
    // Date类可以访问Person的私有成员
    d.displayPersonInfo(p);
    
    return 0;
}

5.5 共享数据的保护

        在多对象共享数据时,需要采取保护措施防止数据被意外修改,const关键字是实现这一目标的重要工具。

5.5.1 常对象

常对象的所有成员的值在对象生命周期内不能被修改,只能调用常成员函数。

常对象示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

class Rectangle {
private:
    double length;
    double width;
    
public:
    // 构造函数
    Rectangle(double l, double w) : length(l), width(w) {}
    
    // 常成员函数:不修改成员变量
    double getArea() const {
        return length * width;
    }
    
    // 非常成员函数:修改成员变量
    void setDimensions(double l, double w) {
        length = l;
        width = w;
    }
};

int main() {
    // 非常对象:可以调用所有成员函数
    Rectangle rect1(5, 3);
    cout << "rect1面积: " << rect1.getArea() << endl;
    rect1.setDimensions(6, 4); // 合法
    cout << "修改后rect1面积: " << rect1.getArea() << endl;
    
    // 常对象:只能调用常成员函数
    const Rectangle rect2(4, 2);
    cout << "rect2面积: " << rect2.getArea() << endl;
    // rect2.setDimensions(5, 3); // 错误:常对象不能调用非常成员函数
    
    return 0;
}
5.5.2 用 const 修饰的类成员

  const可以修饰类的数据成员和成员函数,分别称为常数据成员和常成员函数。

const 修饰类成员示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

class Circle {
private:
    double radius;
    // 常数据成员:初始化后不能修改
    const double PI; 
    
public:
    // 构造函数初始化列表初始化常数据成员
    Circle(double r) : radius(r), PI(3.14159) {}
    
    // 常成员函数:不能修改成员变量
    double getArea() const {
        // radius = 5; // 错误:常成员函数不能修改普通成员
        return PI * radius * radius;
    }
    
    // 非常成员函数:可以修改成员变量
    void setRadius(double r) {
        radius = r;
    }
    
    // mutable成员变量:即使在常成员函数中也可以修改
    mutable int accessCount;
    
    // 统计访问次数的常成员函数
    void countAccess() const {
        accessCount++; // 合法:mutable成员可以在常函数中修改
    }
};

int main() {
    Circle c(5);
    cout << "圆面积: " << c.getArea() << endl;
    
    c.setRadius(6);
    cout << "修改半径后面积: " << c.getArea() << endl;
    
    // 测试mutable成员
    c.accessCount = 0;
    c.countAccess();
    c.countAccess();
    cout << "面积被访问次数: " << c.accessCount << endl;
    
    return 0;
}
5.5.3 常引用

        常引用(const &)指向的对象不能通过引用被修改,常用于函数参数,避免对象拷贝同时防止数据被修改。

常引用示例
代码语言:javascript
复制
#include <iostream>
#include <string>
using namespace std;

class Book {
private:
    string title;
    double price;
    
public:
    Book(string t, double p) : title(t), price(p) {}
    
    // 常成员函数
    string getTitle() const { return title; }
    double getPrice() const { return price; }
    
    // 非常成员函数
    void setPrice(double p) { price = p; }
};

// 使用常引用作为参数:避免拷贝,且不能修改原对象
void displayBookInfo(const Book& book) {
    cout << "书名: " << book.getTitle() << endl;
    cout << "价格: " << book.getPrice() << endl;
    // book.setPrice(50); // 错误:不能通过常引用修改对象
}

// 使用普通引用作为参数:可以修改原对象
void discountBook(Book& book, double discount) {
    double newPrice = book.getPrice() * (1 - discount);
    book.setPrice(newPrice);
}

int main() {
    Book book("C++程序设计", 49.9);
    
    // 常引用
    const Book& constRef = book;
    cout << "通过常引用访问: " << constRef.getTitle() << endl;
    // constRef.setPrice(59.9); // 错误:常引用不能修改对象
    
    // 普通引用
    Book& ref = book;
    ref.setPrice(59.9); // 合法
    cout << "修改后价格: " << book.getPrice() << endl;
    
    // 函数中的常引用参数
    displayBookInfo(book);
    
    // 函数中的普通引用参数
    discountBook(book, 0.1); // 10%折扣
    cout << "折扣后价格: " << book.getPrice() << endl;
    
    return 0;
}

5.6 多文件结构和编译预处理命令

        大型 C++ 程序通常采用多文件结构,将类的声明、实现和主函数分别放在不同文件中,提高代码的可维护性。

5.6.1 C++ 程序的一般组织结构

典型的 C++ 程序结构:

  • 头文件(.h 或.hpp):类的声明、函数原型、宏定义等
  • 源文件(.cpp):类成员函数的实现、全局函数定义等
  • 主程序文件(通常 main.cpp):程序入口
多文件结构示例

1. 头文件(Student.h)

代码语言:javascript
复制
#ifndef STUDENT_H  // 防止头文件重复包含
#define STUDENT_H

#include <string>
using namespace std;

class Student {
private:
    string name;
    int id;
    static int count; // 静态数据成员声明
    
public:
    Student(string n, int i); // 构造函数声明
    ~Student(); // 析构函数声明
    void display() const; // 常成员函数声明
    static int getCount(); // 静态成员函数声明
};

#endif // STUDENT_H

2. 源文件(Student.cpp)

代码语言:javascript
复制
#include "Student.h"
#include <iostream>

// 静态数据成员初始化
int Student::count = 0;

// 构造函数实现
Student::Student(string n, int i) : name(n), id(i) {
    count++;
}

// 析构函数实现
Student::~Student() {
    count--;
}

// 成员函数实现
void Student::display() const {
    cout << "姓名: " << name << ", 学号: " << id << endl;
}

// 静态成员函数实现
int Student::getCount() {
    return count;
}

3. 主程序文件(main.cpp)

代码语言:javascript
复制
#include "Student.h"
#include <iostream>

int main() {
    cout << "初始学生数量: " << Student::getCount() << endl;
    
    Student s1("张三", 1001);
    cout << "当前学生数量: " << Student::getCount() << endl;
    s1.display();
    
    Student s2("李四", 1002);
    cout << "当前学生数量: " << Student::getCount() << endl;
    s2.display();
    
    {
        Student s3("王五", 1003);
        cout << "当前学生数量: " << Student::getCount() << endl;
        s3.display();
    } // s3在此处销毁
    
    cout << "当前学生数量: " << Student::getCount() << endl;
    
    return 0;
}

编译命令(GCC):g++ main.cpp Student.cpp -o student_management

5.6.2 外部变量与外部函数

在多文件程序中,外部变量和外部函数可以在多个文件中共享,使用extern关键字声明。

外部变量与函数示例

1. 头文件(shared.h)

代码语言:javascript
复制
#ifndef SHARED_H
#define SHARED_H

// 外部变量声明
extern int global_counter;

// 外部函数声明
extern void incrementCounter();
extern void displayCounter();

#endif // SHARED_H

2. 源文件(shared.cpp)

代码语言:javascript
复制
#include "shared.h"
#include <iostream>

// 外部变量定义
int global_counter = 0;

// 外部函数定义
void incrementCounter() {
    global_counter++;
}

void displayCounter() {
    std::cout << "当前计数器值: " << global_counter << std::endl;
}

3. 主程序文件(main.cpp)

代码语言:javascript
复制
#include "shared.h"
#include <iostream>

int main() {
    displayCounter(); // 输出0
    
    incrementCounter();
    incrementCounter();
    displayCounter(); // 输出2
    
    global_counter = 5; // 直接访问外部变量
    displayCounter(); // 输出5
    
    return 0;
}
5.6.4 编译预处理

        编译预处理是在编译前对源代码进行的处理,常用命令包括#include#define#ifdef等。

编译预处理示例
代码语言:javascript
复制
#include <iostream>
using namespace std;

// 宏定义
#define PI 3.14159
#define MAX(a, b) (a > b ? a : b)
#define DEBUG 1 // 调试模式开关

int main() {
    double radius = 5.0;
    double area = PI * radius * radius; // 使用宏常量
    cout << "圆面积: " << area << endl;
    
    int x = 10, y = 20;
    cout << "较大的数: " << MAX(x, y) << endl; // 使用宏函数
    
    // 条件编译:仅在DEBUG为真时执行
#ifdef DEBUG
    cout << "调试信息: x = " << x << ", y = " << y << endl;
    cout << "调试信息: 程序运行中..." << endl;
#endif
    
#ifndef NDEBUG
    cout << "非调试模式未定义,继续执行调试代码" << endl;
#endif
    
    return 0;
}

5.7 综合实例 —— 个人银行账户管理程序

下面是一个综合运用本章知识点的个人银行账户管理程序,包含静态成员、常成员函数、友元、const 修饰符和多文件结构等特性。

综合实例代码

1. 头文件(BankAccount.h)

代码语言:javascript
复制
#ifndef BANKACCOUNT_H
#define BANKACCOUNT_H

#include <string>
using namespace std;

// 前向声明
class BankManager;

class BankAccount {
private:
    string accountNumber; // 账号
    string accountHolder; // 账户持有人
    double balance; // 余额
    static int totalAccounts; // 总账户数
    
public:
    // 构造函数
    BankAccount(string num, string holder, double initialBalance);
    
    // 析构函数
    ~BankAccount();
    
    // 存款
    void deposit(double amount);
    
    // 取款
    bool withdraw(double amount);
    
    // 常成员函数:查询余额
    double getBalance() const;
    
    // 常成员函数:显示账户信息
    void displayInfo() const;
    
    // 获取总账户数(静态成员函数)
    static int getTotalAccounts();
    
    // 声明友元类
    friend class BankManager;
};

class BankManager {
public:
    // 友元类可以访问BankAccount的私有成员
    void transfer(BankAccount& from, BankAccount& to, double amount);
    void displayAccountDetails(const BankAccount& account);
};

#endif // BANKACCOUNT_H

2. 源文件(BankAccount.cpp)

代码语言:javascript
复制
#include "BankAccount.h"
#include <iostream>

// 初始化静态数据成员
int BankAccount::totalAccounts = 0;

// 构造函数实现
BankAccount::BankAccount(string num, string holder, double initialBalance)
    : accountNumber(num), accountHolder(holder), balance(initialBalance) {
    if (initialBalance < 0) {
        cout << "错误:初始余额不能为负数,已设置为0" << endl;
        balance = 0;
    }
    totalAccounts++;
}

// 析构函数实现
BankAccount::~BankAccount() {
    totalAccounts--;
}

// 存款实现
void BankAccount::deposit(double amount) {
    if (amount > 0) {
        balance += amount;
        cout << "存款成功!存入金额: " << amount << endl;
    } else {
        cout << "错误:存款金额必须为正数" << endl;
    }
}

// 取款实现
bool BankAccount::withdraw(double amount) {
    if (amount > 0 && amount <= balance) {
        balance -= amount;
        cout << "取款成功!取出金额: " << amount << endl;
        return true;
    } else if (amount <= 0) {
        cout << "错误:取款金额必须为正数" << endl;
        return false;
    } else {
        cout << "错误:余额不足" << endl;
        return false;
    }
}

// 获取余额实现
double BankAccount::getBalance() const {
    return balance;
}

// 显示账户信息实现
void BankAccount::displayInfo() const {
    cout << "账号: " << accountNumber << endl;
    cout << "持有人: " << accountHolder << endl;
    cout << "余额: " << balance << " 元" << endl;
}

// 获取总账户数实现
int BankAccount::getTotalAccounts() {
    return totalAccounts;
}

// 转账实现
void BankManager::transfer(BankAccount& from, BankAccount& to, double amount) {
    cout << "\n尝试转账 " << amount << " 元从账号 " 
         << from.accountNumber << " 到账号 " << to.accountNumber << endl;
         
    if (amount <= 0) {
        cout << "转账失败:金额必须为正数" << endl;
        return;
    }
    
    if (from.balance >= amount) {
        from.balance -= amount;
        to.balance += amount;
        cout << "转账成功!" << endl;
    } else {
        cout << "转账失败:余额不足" << endl;
    }
}

// 显示账户详细信息实现
void BankManager::displayAccountDetails(const BankAccount& account) {
    cout << "\n===== 账户详细信息 =====" << endl;
    cout << "账号: " << account.accountNumber << endl;
    cout << "持有人: " << account.accountHolder << endl;
    cout << "当前余额: " << account.balance << " 元" << endl;
    cout << "======================" << endl;
}

3. 主程序文件(main.cpp)

代码语言:javascript
复制
#include "BankAccount.h"
#include <iostream>

int main() {
    cout << "===== 个人银行账户管理系统 =====" << endl;
    
    // 显示初始账户数
    cout << "\n初始账户总数: " << BankAccount::getTotalAccounts() << endl;
    
    // 创建账户
    BankAccount acc1("ACC001", "张三", 1000.0);
    BankAccount acc2("ACC002", "李四", 500.0);
    
    // 显示账户总数
    cout << "\n当前账户总数: " << BankAccount::getTotalAccounts() << endl;
    
    // 显示账户信息
    cout << "\n===== 账户信息 =====" << endl;
    acc1.displayInfo();
    cout << endl;
    acc2.displayInfo();
    
    // 存款和取款操作
    cout << "\n===== 账户操作 =====" << endl;
    acc1.deposit(500);
    cout << "acc1 当前余额: " << acc1.getBalance() << endl;
    
    acc1.withdraw(300);
    cout << "acc1 当前余额: " << acc1.getBalance() << endl;
    
    acc2.withdraw(1000); // 余额不足的情况
    
    // 使用友元类进行转账操作
    BankManager manager;
    manager.transfer(acc1, acc2, 400);
    
    // 使用常引用作为参数的函数调用
    const BankAccount& constAccRef = acc1;
    cout << "\nacc1 当前余额: " << constAccRef.getBalance() << endl;
    // constAccRef.deposit(100); // 错误:常引用不能调用非常成员函数
    
    // 显示账户详细信息
    manager.displayAccountDetails(acc1);
    manager.displayAccountDetails(acc2);
    
    // 离开作用域前的账户总数
    cout << "\n程序结束前账户总数: " << BankAccount::getTotalAccounts() << endl;
    
    return 0;
}

编译命令:g++ main.cpp BankAccount.cpp -o bank_management

运行结果:

代码语言:javascript
复制
===== 个人银行账户管理系统 =====

初始账户总数: 0

当前账户总数: 2

===== 账户信息 =====
账号: ACC001
持有人: 张三
余额: 1000 元

账号: ACC002
持有人: 李四
余额: 500 元

===== 账户操作 =====
存款成功!存入金额: 500
acc1 当前余额: 1500
取款成功!取出金额: 300
acc1 当前余额: 1200
取款成功!取出金额: 400
acc1 当前余额: 800

尝试转账 400 元从账号 ACC001 到账号 ACC002
转账成功!

acc1 当前余额: 800

===== 账户详细信息 =====
账号: ACC001
持有人: 张三
当前余额: 800 元
======================

===== 账户详细信息 =====
账号: ACC002
持有人: 李四
当前余额: 900 元
======================

程序结束前账户总数: 2

5.8 深度探索

5.8.1 常成员函数的声明原则

常成员函数的声明需要遵循以下原则:

  1. const关键字放在函数参数列表之后
  2. 常成员函数不能修改类的非静态数据成员(除非成员被mutable修饰)
  3. 常成员函数不能调用非常成员函数
  4. 常对象只能调用常成员函数
  5. 非常对象可以调用常成员函数和非常成员函数
常成员函数声明原则对比表

函数类型

常对象能否调用

能否修改普通成员

能否调用非常成员函数

非常成员函数

❌ 不能

✅ 能

✅ 能

常成员函数

✅ 能

❌ 不能(除非成员为 mutable)

❌ 不能

5.8.2 代码的编译、连接与执行过程

C++ 程序从源代码到可执行文件需要经过预处理、编译、汇编和链接四个阶段。

编译连接流程图
详细过程说明:

1. 预处理阶段 (Preprocessing)

  • 输入:.cpp/.h源文件
  • 处理:
    • #include文件插入(递归处理)
    • #define宏替换(文本级操作)
    • 条件编译(#ifdef/#endif
    • 删除注释
  • 输出:纯净的.i文本文件(无宏/指令)

2. 编译阶段 (Compilation)

  • 输入:预处理后的.i文件
  • 处理流程:
  • 输出:平台相关的汇编代码(.s文件)

3. 汇编阶段 (Assembly)

  • 输入:汇编代码(.s文件)
  • 处理:将汇编指令转换为机器码
  • 输出:目标文件(.o.obj二进制文件)

4. 链接阶段 (Linking)

  • 输入:多个目标文件 + 库文件
  • 关键操作:
    • 符号解析:匹配声明与实现
    • 重定位:合并代码段/数据段
    • 地址绑定:分配绝对地址
  • 库处理:
    • 静态库(.a/.lib):直接嵌入可执行文件
    • 动态库(.dll/.so):生成引用占位符
  • 输出:可执行文件(.exe/.out

5. 执行阶段 (Execution)

  • 加载过程:
  • 运行时特性:
    • 栈:自动管理局部变量
    • 堆:动态内存分配(new/delete)
    • 全局区:存储全局/静态变量
  • 结束:main返回后系统回收资源
关键文件类型:

阶段

输入文件

输出文件

工具示例

预处理

.cpp/.h

.i

g++ -E, cl /P

编译

.i

.s

g++ -S, cl /FA

汇编

.s

.o/.obj

as, ml

链接

.o/.obj + .a/.lib

.exe/.out

ld, link

动态链接

.exe + .so/.dll

内存映像

ld-linux.so

常见错误示例:
  • 编译期:语法错误、类型不匹配
  • 链接期:未定义引用(undefined reference)、多重定义
  • 运行时:段错误(内存违规)、动态库未找到

通过-v编译选项(如g++ -v)可查看详细过程。理解此流程有助于诊断构建错误和优化程序结构。

5.9 小结

本章深入探讨了 C++ 中数据共享与保护的核心机制,主要知识点包括:

  1. 标识符的作用域与可见性:理解不同作用域(块、函数、类、文件)的标识符有效范围和访问规则。
  2. 对象的生存期:区分静态生存期(程序运行期间始终存在)和动态生存期(进入作用域创建,离开作用域销毁)的对象特性。
  3. 类的静态成员:静态数据成员实现类级别的数据共享,静态函数成员提供类级别的操作接口,不依赖具体对象。
  4. 类的友元:友元函数和友元类提供了访问私有成员的灵活性,但需谨慎使用以避免破坏封装性。
  5. 共享数据的保护const关键字用于定义常对象、常成员和常引用,有效防止数据被意外修改。
  6. 多文件结构:合理组织头文件和源文件,使用extern声明外部变量和函数,提高代码的可维护性。
  7. 编译预处理#include#define和条件编译等预处理命令增强了代码的灵活性和可移植性。

        通过本章学习,读者应能熟练运用这些机制设计出数据共享安全、结构清晰的 C++ 程序,为后续面向对象编程打下坚实基础。

结语

        数据的共享与保护是 C++ 面向对象编程的重要概念,合理运用本章介绍的技术可以提高程序的安全性、可读性和可维护性。建议读者结合实例代码多做练习,深入理解每个知识点的应用场景和注意事项。如果有任何问题或建议,欢迎在评论区留言讨论!


        希望本文对您学习 C++ 有所帮助,如果觉得有用,请点赞、收藏并关注哦!后续会持续更新 C++ 程序设计相关内容。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  •  引言
  • 本章知识思维导图
  • 5.1 标识符的作用域与可见性
    • 5.1.1 作用域
      • 作用域示例代码
    • 5.1.2 可见性
      • 可见性与标识符隐藏示例
  • 5.2 对象的生存期
    • 5.2.1 静态生存期
      • 静态生存期示例
    • 5.2.2 动态生存期
      • 动态生存期与静态生存期对比流程图
    • 关键对比说明:
    • 流程详解:
  • 5.3 类的静态成员
    • 5.3.1 静态数据成员
      • 静态数据成员示例
    • 5.3.2 静态函数成员
      • 静态函数成员示例
  • 5.4 类的友元
    • 5.4.1 友元函数
      • 友元函数示例
    • 5.4.2 友元类
      • 友元类示例
  • 5.5 共享数据的保护
    • 5.5.1 常对象
      • 常对象示例
    • 5.5.2 用 const 修饰的类成员
      • const 修饰类成员示例
    • 5.5.3 常引用
      • 常引用示例
  • 5.6 多文件结构和编译预处理命令
    • 5.6.1 C++ 程序的一般组织结构
      • 多文件结构示例
    • 5.6.2 外部变量与外部函数
      • 外部变量与函数示例
    • 5.6.4 编译预处理
      • 编译预处理示例
  • 5.7 综合实例 —— 个人银行账户管理程序
    • 综合实例代码
  • 5.8 深度探索
    • 5.8.1 常成员函数的声明原则
      • 常成员函数声明原则对比表
    • 5.8.2 代码的编译、连接与执行过程
      • 编译连接流程图
    • 详细过程说明:
    • 关键文件类型:
    • 常见错误示例:
  • 5.9 小结
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档