C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 类和对象

C++ 类和对象从基础语法到高级特性深度解析

作者:xie_pin_an

本文深入探讨了C++类和对象的核心概念,包括类的定义、访问控制、实例化、this指针、默认成员函数(构造、析构、拷贝构造、赋值重载)、以及高级特性,感兴趣的朋友跟随小编一起看看吧

在 C++ 编程中,类和对象是面向对象编程(OOP)的核心基石,封装、继承、多态三大特性均围绕其展开。本文将从类的定义与实例化、默认成员函数、高级特性等维度,结合实战代码,系统梳理类和对象的关键知识点,帮助开发者夯实 OOP 基础。

一、类的基础认知:定义、访问控制与实例化

1.1 类的定义格式

C++ 使用class关键字定义类(struct也可定义类,兼容 C 语言用法且支持成员函数),类体包含成员变量(属性)和成员函数(方法),结束时必须加分号。为区分成员变量与局部变量,惯例是给成员变量加前缀(如_)或后缀,例如_yearm_month

class Date {
public:
    // 成员函数:初始化日期
    void Init(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    // 成员变量:加前缀_区分
    int _year;
    int _month;
    int _day;
}; // 分号不可省略

1.2 访问限定符:封装的核心实现

访问限定符控制成员的访问权限,是封装特性的直接体现:

1.3 类域与成员函数分离

类定义了独立的作用域,类外实现成员函数时需用::作用域操作符指明所属类,否则编译器会视为全局函数。

// 类内声明,类外实现
void Date::Init(int year, int month, int day) {
    _year = year;
    _month = month;
    _day = day;
}

1.4 类的实例化

类是对象的 “设计图”,仅声明成员变量(未分配空间),实例化是通过类创建对象并分配物理内存的过程。一个类可实例化多个对象,每个对象拥有独立的成员变量存储空间,成员函数则共享(存储在代码段)。

int main() {
    Date d1; // 实例化对象d1,分配内存
    d1.Init(2024, 10, 1); // 调用成员函数初始化
    return 0;
}

1.5 对象大小计算

对象仅存储成员变量,大小遵循内存对齐规则(与结构体一致),目的是提高访问效率:

  1. 第一个成员偏移量为 0;
  2. 其他成员对齐到 “对齐数”(编译器默认值与成员大小的较小值,VS 默认 8)的整数倍;
  3. 总大小为最大对齐数的整数倍;
  4. 无成员变量的类对象大小为 1 字节(占位标识对象存在)。
class A {
private:
    char _ch; // 1字节
    int _i;   // 4字节,对齐数4
};
// 内存对齐后大小:8字节(1+3填充+4)
cout << sizeof(A) << endl; // 输出8

二、this 指针:对象的 “隐藏身份标识”

2.1 核心问题

多个对象共享成员函数,函数如何区分操作的是哪个对象?例如d1.Init()d2.Init(),函数需知道当前操作的是d1还是d2

2.2 本质与特性

C++ 编译器在成员函数形参第一个位置隐含添加this指针(类型为类名* const),指向当前调用函数的对象,函数体内访问成员变量本质是通过this指针访问(可显式使用,不可显式声明)。

// 编译器优化后的Init函数原型
void Date::Init(Date* const this, int year, int month, int day) {
    this->_year = year; // 显式使用this
    _month = month;    // 隐式使用this
}
// 调用时编译器自动传递对象地址
d1.Init(2024, 10, 1); // 等价于d1.Init(&d1, 2024, 10, 1)

2.3 经典面试题解析

// 题目1:编译运行结果?
class A {
public:
    void Print() { cout << "A::Print()" << endl; }
private:
    int _a;
};
int main() {
    A* p = nullptr;
    p->Print(); // 正常运行:Print未访问成员变量,无需解引用p
    return 0;
}
// 题目2:编译运行结果?
class A {
public:
    void Print() { cout << _a << endl; } // 访问成员变量,需解引用this
private:
    int _a;
};
int main() {
    A* p = nullptr;
    p->Print(); // 运行崩溃:this为nullptr,解引用出错
    return 0;
}

三、默认成员函数:编译器的 “自动实现”

当用户未显式定义时,编译器会自动生成 6 个默认成员函数,核心是前 4 个:构造、析构、拷贝构造、赋值重载。

3.1 构造函数:对象的 “初始化器”

class Date {
public:
    // 全缺省构造(推荐,兼顾多种初始化场景)
    Date(int year = 1, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};
// 实例化方式
Date d1; // 调用全缺省构造,默认1-1-1
Date d2(2024, 10, 1); // 调用带参构造

3.2 析构函数:对象的 “清理工”

class Stack {
public:
    Stack(int n = 4) {
        _a = (int*)malloc(sizeof(int) * n);
        _capacity = n;
        _top = 0;
    }
    // 显式定义析构,释放堆内存
    ~Stack() {
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    int* _a;
    size_t _capacity;
    size_t _top;
};

3.3 拷贝构造函数:对象的 “复制器”

Stack::Stack(const Stack& st) {
    // 深拷贝:为新对象分配独立内存
    _a = (int*)malloc(sizeof(int) * st._capacity);
    if (_a == nullptr) perror("malloc fail");
    memcpy(_a, st._a, sizeof(int) * st._top);
    _top = st._top;
    _capacity = st._capacity;
}

3.4 赋值运算符重载:对象的 “赋值器”

Date& Date::operator=(const Date& d) {
    if (this != &d) { // 避免自赋值
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this; // 支持连续赋值
}

四、高级特性:提升代码灵活性与效率

4.1 初始化列表:成员变量的 “初始化源头”

class Date {
public:
    // 初始化列表初始化
    Date(int year, int month, int day, int& ref)
        : _year(year)
        , _month(month)
        , _day(day)
        , _ref(ref) // 引用必须初始化
        , _n(10)    // const成员必须初始化
    {}
private:
    int _year;
    int _month;
    int _day;
    int& _ref;    // 引用成员
    const int _n; // const成员
};

4.2 static 成员:类的 “共享资源”

应用场景:统计对象创建个数:

class A {
public:
    A() { ++_scount; }
    A(const A&) { ++_scount; }
    ~A() { --_scount; }
    static int GetCount() { return _scount; } // 静态成员函数
private:
    static int _scount; // 静态成员变量
};
int A::_scount = 0; // 类外初始化
int main() {
    A a1, a2;
    cout << A::GetCount() << endl; // 输出2
    return 0;
}

4.3 友元:突破封装的 “特殊权限”

友元允许外部函数 / 类访问类的私有成员,分为友元函数和友元类,慎用(破坏封装)

class Date {
    // 友元声明:operator<<可访问私有成员
    friend ostream& operator<<(ostream& out, const Date& d);
private:
    int _year;
    int _month;
    int _day;
};
// 全局函数实现
ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "-" << d._month << "-" << d._day;
    return out;
}
// 使用
Date d(2024, 10, 1);
cout << d << endl; // 输出2024-10-1

4.4 匿名对象:临时使用的 “无名称对象”

class Solution {
public:
    int Sum(int n) { return n*(n+1)/2; }
};
// 匿名对象调用函数
int ret = Solution().Sum(100); // 输出5050

五、C++ vs C 语言:封装的优势

Stack为例,对比 C 语言与 C++ 的实现差异:

特性C 语言实现C++ 实现
数据与函数关系分离(函数需显式传结构体指针)封装(数据 + 函数在类内,this 指针隐式传递)
访问控制无,可直接修改结构体成员访问限定符控制,私有成员不可直接修改
初始化与清理需手动调用Init/Destroy构造 / 析构函数自动调用,避免遗漏
代码简洁性需 typedef,函数参数繁琐类名直接作为类型,语法更简洁

六、总结

类和对象是 C++ 面向对象编程的核心,核心要点可概括为:

  1. 封装:通过类整合数据与函数,访问限定符控制权限;
  2. 默认成员函数:构造(初始化)、析构(清理)、拷贝构造(复制新对象)、赋值重载(对象赋值)是基础,需区分使用场景;
  3. 高级特性:初始化列表解决特殊成员初始化,static 成员实现共享资源,友元灵活访问私有成员(慎用),匿名对象简化临时操作;
  4. 内存与效率:理解 this 指针、内存对齐、编译器拷贝优化,避免内存泄漏和性能问题。

到此这篇关于C++ 类和对象全解析:从基础语法到高级特性的文章就介绍到这了,更多相关C++ 类和对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文