老关耳 發表於 2026-1-10 11:43:41

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、类的基础认知:定义、访问控制与实例化</li><ul class="second_class_ul"><li>1.1 类的定义格式</li><li>1.2 访问限定符:封装的核心实现</li><li>1.3 类域与成员函数分离</li><li>1.4 类的实例化</li><li>1.5 对象大小计算</li></ul><li>二、this 指针:对象的 &ldquo;隐藏身份标识&rdquo;</li><ul class="second_class_ul"><li>2.1 核心问题</li><li>2.2 本质与特性</li><li>2.3 经典面试题解析</li></ul><li>三、默认成员函数:编译器的 &ldquo;自动实现&rdquo;</li><ul class="second_class_ul"><li>3.1 构造函数:对象的 &ldquo;初始化器&rdquo;</li><li>3.2 析构函数:对象的 &ldquo;清理工&rdquo;</li><li>3.3 拷贝构造函数:对象的 &ldquo;复制器&rdquo;</li><li>3.4 赋值运算符重载:对象的 &ldquo;赋值器&rdquo;</li></ul><li>四、高级特性:提升代码灵活性与效率</li><ul class="second_class_ul"><li>4.1 初始化列表:成员变量的 &ldquo;初始化源头&rdquo;</li><li>4.2 static 成员:类的 &ldquo;共享资源&rdquo;</li><li>4.3 友元:突破封装的 &ldquo;特殊权限&rdquo;</li><li>4.4 匿名对象:临时使用的 &ldquo;无名称对象&rdquo;</li></ul><li>五、C++ vs C 语言:封装的优势</li><ul class="second_class_ul"></ul><li>六、总结</li><ul class="second_class_ul"></ul></ul></div><p>在 C++ 编程中,类和对象是面向对象编程(OOP)的核心基石,封装、继承、多态三大特性均围绕其展开。本文将从类的定义与实例化、默认成员函数、高级特性等维度,结合实战代码,系统梳理类和对象的关键知识点,帮助开发者夯实 OOP 基础。</p>
<p class="maodian"></p><h2>一、类的基础认知:定义、访问控制与实例化</h2>
<p class="maodian"></p><h3>1.1 类的定义格式</h3>
<p>C++ 使用<code>class</code>关键字定义类(<code>struct</code>也可定义类,兼容 C 语言用法且支持成员函数),类体包含成员变量(属性)和成员函数(方法),结束时必须加<strong>分号</strong>。为区分成员变量与局部变量,惯例是给成员变量加前缀(如<code>_</code>)或后缀,例如<code>_year</code>、<code>m_month</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">class Date {
public:
    // 成员函数:初始化日期
    void Init(int year, int month, int day) {
      _year = year;
      _month = month;
      _day = day;
    }
private:
    // 成员变量:加前缀_区分
    int _year;
    int _month;
    int _day;
}; // 分号不可省略</pre></div>
<p class="maodian"></p><h3>1.2 访问限定符:封装的核心实现</h3>
<p>访问限定符控制成员的访问权限,是封装特性的直接体现:</p>
<ul><li><code>public</code>:类外可直接访问(通常暴露接口函数);</li><li><code>private</code>/<code>protected</code>:类外不可直接访问(通常隐藏成员变量,继承时二者有差异);</li><li>访问权限作用域:从限定符出现位置到下一个限定符结束,<code>class</code>默认<code>private</code>,<code>struct</code>默认<code>public</code>。</li></ul>
<p class="maodian"></p><h3>1.3 类域与成员函数分离</h3>
<p>类定义了独立的作用域,类外实现成员函数时需用<code>::</code>作用域操作符指明所属类,否则编译器会视为全局函数。</p>
<div class="jb51code"><pre class="brush:cpp;">// 类内声明,类外实现
void Date::Init(int year, int month, int day) {
    _year = year;
    _month = month;
    _day = day;
}</pre></div>
<p class="maodian"></p><h3>1.4 类的实例化</h3>
<p>类是对象的 &ldquo;设计图&rdquo;,仅声明成员变量(未分配空间),<strong>实例化</strong>是通过类创建对象并分配物理内存的过程。一个类可实例化多个对象,每个对象拥有独立的成员变量存储空间,成员函数则共享(存储在代码段)。</p>
<div class="jb51code"><pre class="brush:cpp;">int main() {
    Date d1; // 实例化对象d1,分配内存
    d1.Init(2024, 10, 1); // 调用成员函数初始化
    return 0;
}</pre></div>
<p class="maodian"></p><h3>1.5 对象大小计算</h3>
<p>对象仅存储成员变量,大小遵循<strong>内存对齐规则</strong>(与结构体一致),目的是提高访问效率:</p>
<ol><li>第一个成员偏移量为 0;</li><li>其他成员对齐到 &ldquo;对齐数&rdquo;(编译器默认值与成员大小的较小值,VS 默认 8)的整数倍;</li><li>总大小为最大对齐数的整数倍;</li><li>无成员变量的类对象大小为 1 字节(占位标识对象存在)。</li></ol>
<div class="jb51code"><pre class="brush:cpp;">class A {
private:
    char _ch; // 1字节
    int _i;   // 4字节,对齐数4
};
// 内存对齐后大小:8字节(1+3填充+4)
cout &lt;&lt; sizeof(A) &lt;&lt; endl; // 输出8</pre></div>
<p class="maodian"></p><h2>二、this 指针:对象的 &ldquo;隐藏身份标识&rdquo;</h2>
<p class="maodian"></p><h3>2.1 核心问题</h3>
<p>多个对象共享成员函数,函数如何区分操作的是哪个对象?例如<code>d1.Init()</code>和<code>d2.Init()</code>,函数需知道当前操作的是<code>d1</code>还是<code>d2</code>。</p>
<p class="maodian"></p><h3>2.2 本质与特性</h3>
<p>C++ 编译器在成员函数形参第一个位置隐含添加<code>this</code>指针(类型为<code>类名* const</code>),指向当前调用函数的对象,函数体内访问成员变量本质是通过<code>this</code>指针访问(可显式使用,不可显式声明)。</p>
<div class="jb51code"><pre class="brush:cpp;">// 编译器优化后的Init函数原型
void Date::Init(Date* const this, int year, int month, int day) {
    this-&gt;_year = year; // 显式使用this
    _month = month;    // 隐式使用this
}
// 调用时编译器自动传递对象地址
d1.Init(2024, 10, 1); // 等价于d1.Init(&amp;d1, 2024, 10, 1)</pre></div>
<p class="maodian"></p><h3>2.3 经典面试题解析</h3>
<div class="jb51code"><pre class="brush:cpp;">// 题目1:编译运行结果?
class A {
public:
    void Print() { cout &lt;&lt; "A::Print()" &lt;&lt; endl; }
private:
    int _a;
};
int main() {
    A* p = nullptr;
    p-&gt;Print(); // 正常运行:Print未访问成员变量,无需解引用p
    return 0;
}
// 题目2:编译运行结果?
class A {
public:
    void Print() { cout &lt;&lt; _a &lt;&lt; endl; } // 访问成员变量,需解引用this
private:
    int _a;
};
int main() {
    A* p = nullptr;
    p-&gt;Print(); // 运行崩溃:this为nullptr,解引用出错
    return 0;
}</pre></div>
<p class="maodian"></p><h2>三、默认成员函数:编译器的 &ldquo;自动实现&rdquo;</h2>
<p>当用户未显式定义时,编译器会自动生成 6 个默认成员函数,核心是前 4 个:构造、析构、拷贝构造、赋值重载。</p>
<p class="maodian"></p><h3>3.1 构造函数:对象的 &ldquo;初始化器&rdquo;</h3>
<ul><li><strong>功能</strong>:替代<code>Init</code>函数,对象实例化时自动调用,初始化成员变量;</li><li><strong>特性</strong>:<ol><li>函数名与类名相同,无返回值(无需写<code>void</code>);</li><li>可重载(无参、带参、全缺省);</li><li>无显式定义时,编译器生成默认构造(对内置类型不初始化,自定义类型调用其默认构造);</li><li>无参、全缺省、编译器默认生成的构造,统称 &ldquo;默认构造&rdquo;(不传参即可调用)。</li></ol></li></ul>
<div class="jb51code"><pre class="brush:cpp;">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); // 调用带参构造</pre></div>
<p class="maodian"></p><h3>3.2 析构函数:对象的 &ldquo;清理工&rdquo;</h3>
<ul><li><strong>功能</strong>:替代<code>Destroy</code>函数,对象生命周期结束时自动调用,释放资源(如堆内存);</li><li><strong>特性</strong>:<ol><li>函数名<code>~类名</code>,无参数无返回值;</li><li>一个类仅一个析构函数(不可重载);</li><li>无显式定义时,编译器生成默认析构(对内置类型不处理,自定义类型调用其析构);</li><li>有资源申请(如<code>malloc</code>、<code>new</code>)时必须显式定义,否则内存泄漏。</li></ol></li></ul>
<div class="jb51code"><pre class="brush:cpp;">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;
};</pre></div>
<p class="maodian"></p><h3>3.3 拷贝构造函数:对象的 &ldquo;复制器&rdquo;</h3>
<ul><li><strong>功能</strong>:用已有对象初始化新对象(如<code>Date d2 = d1</code>);</li><li><strong>特性</strong>:<ol><li>第一个参数必须是<code>const 类名&amp;</code>(传值会引发无穷递归),后续参数可带默认值;</li><li>无显式定义时,编译器生成默认拷贝构造(内置类型值拷贝 / 浅拷贝,自定义类型调用其拷贝构造);</li><li>浅拷贝问题:若成员变量指向堆内存(如<code>Stack</code>的<code>_a</code>),会导致多个对象共享同一块内存,析构时重复释放崩溃,需显式实现深拷贝。</li></ol></li></ul>
<div class="jb51code"><pre class="brush:cpp;">Stack::Stack(const Stack&amp; 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;
}</pre></div>
<p class="maodian"></p><h3>3.4 赋值运算符重载:对象的 &ldquo;赋值器&rdquo;</h3>
<ul><li><strong>功能</strong>:两个已存在对象间的赋值(如<code>d1 = d2</code>),区别于拷贝构造(初始化新对象);</li><li><strong>特性</strong>:<ol><li>必须重载为成员函数,函数原型<code>类名&amp; operator=(const 类名&amp;)</code>;</li><li>返回<code>类名&amp;</code>支持连续赋值(如<code>d1 = d2 = d3</code>);</li><li>需检查自赋值(<code>if (this != &amp;d)</code>),避免重复释放;</li><li>无显式定义时,编译器生成默认赋值重载(浅拷贝,需资源的类需显式实现深拷贝)。</li></ol></li></ul>
<div class="jb51code"><pre class="brush:cpp;">Date&amp; Date::operator=(const Date&amp; d) {
    if (this != &amp;d) { // 避免自赋值
      _year = d._year;
      _month = d._month;
      _day = d._day;
    }
    return *this; // 支持连续赋值
}</pre></div>
<p class="maodian"></p><h2>四、高级特性:提升代码灵活性与效率</h2>
<p class="maodian"></p><h3>4.1 初始化列表:成员变量的 &ldquo;初始化源头&rdquo;</h3>
<ul><li><strong>格式</strong>:构造函数后加<code>:</code>,后跟成员变量初始化表达式(<code>成员变量(值), ...</code>);</li><li><strong>必要性</strong>:引用成员、<code>const</code>成员、无默认构造的自定义类型成员,必须通过初始化列表初始化;</li><li><strong>注意</strong>:初始化顺序与类中成员声明顺序一致,与列表顺序无关。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">class Date {
public:
    // 初始化列表初始化
    Date(int year, int month, int day, int&amp; ref)
      : _year(year)
      , _month(month)
      , _day(day)
      , _ref(ref) // 引用必须初始化
      , _n(10)    // const成员必须初始化
    {}
private:
    int _year;
    int _month;
    int _day;
    int&amp; _ref;    // 引用成员
    const int _n; // const成员
};</pre></div>
<p class="maodian"></p><h3>4.2 static 成员:类的 &ldquo;共享资源&rdquo;</h3>
<ul><li><strong>静态成员变量</strong>:
<ol><li>用<code>static</code>修饰,所有对象共享,存储在静态区;</li><li>类内声明,类外初始化(<code>类型 类名::变量名 = 值</code>);</li><li>受访问限定符控制,可通过<code>类名::变量</code>或<code>对象.变量</code>访问。</li></ol></li><li><strong>静态成员函数</strong>:<ol><li>用<code>static</code>修饰,无<code>this</code>指针;</li><li>仅可访问静态成员变量 / 函数,不可访问非静态成员。</li></ol></li></ul>
<p>应用场景:统计对象创建个数:</p>
<div class="jb51code"><pre class="brush:cpp;">class A {
public:
    A() { ++_scount; }
    A(const A&amp;) { ++_scount; }
    ~A() { --_scount; }
    static int GetCount() { return _scount; } // 静态成员函数
private:
    static int _scount; // 静态成员变量
};
int A::_scount = 0; // 类外初始化
int main() {
    A a1, a2;
    cout &lt;&lt; A::GetCount() &lt;&lt; endl; // 输出2
    return 0;
}</pre></div>
<p class="maodian"></p><h3>4.3 友元:突破封装的 &ldquo;特殊权限&rdquo;</h3>
<p>友元允许外部函数 / 类访问类的私有成员,分为友元函数和友元类,<strong>慎用(破坏封装)</strong>。</p>
<ul><li><strong>友元函数</strong>:非成员函数,类内声明时加<code>friend</code>;</li><li><strong>友元类</strong>:类 A 是类 B 的友元,则 A 的所有成员函数可访问 B 的私有成员(单向关系,不可传递)。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">class Date {
    // 友元声明:operator&lt;&lt;可访问私有成员
    friend ostream&amp; operator&lt;&lt;(ostream&amp; out, const Date&amp; d);
private:
    int _year;
    int _month;
    int _day;
};
// 全局函数实现
ostream&amp; operator&lt;&lt;(ostream&amp; out, const Date&amp; d) {
    out &lt;&lt; d._year &lt;&lt; "-" &lt;&lt; d._month &lt;&lt; "-" &lt;&lt; d._day;
    return out;
}
// 使用
Date d(2024, 10, 1);
cout &lt;&lt; d &lt;&lt; endl; // 输出2024-10-1</pre></div>
<p class="maodian"></p><h3>4.4 匿名对象:临时使用的 &ldquo;无名称对象&rdquo;</h3>
<ul><li><strong>格式</strong>:<code>类名(实参)</code>,生命周期仅当前行;</li><li><strong>场景</strong>:临时调用成员函数,无需定义命名对象。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">class Solution {
public:
    int Sum(int n) { return n*(n+1)/2; }
};
// 匿名对象调用函数
int ret = Solution().Sum(100); // 输出5050</pre></div>
<p class="maodian"></p><h2>五、C++ vs C 语言:封装的优势</h2>
<p>以<code>Stack</code>为例,对比 C 语言与 C++ 的实现差异:</p>
<table><thead><tr><th>特性</th><th>C 语言实现</th><th>C++ 实现</th></tr></thead><tbody><tr><td>数据与函数关系</td><td>分离(函数需显式传结构体指针)</td><td>封装(数据 + 函数在类内,this 指针隐式传递)</td></tr><tr><td>访问控制</td><td>无,可直接修改结构体成员</td><td>访问限定符控制,私有成员不可直接修改</td></tr><tr><td>初始化与清理</td><td>需手动调用<code>Init</code>/<code>Destroy</code></td><td>构造 / 析构函数自动调用,避免遗漏</td></tr><tr><td>代码简洁性</td><td>需 typedef,函数参数繁琐</td><td>类名直接作为类型,语法更简洁</td></tr></tbody></table>
<p class="maodian"></p><h2>六、总结</h2>
<p>类和对象是 C++ 面向对象编程的核心,核心要点可概括为:</p>
<ol><li><strong>封装</strong>:通过类整合数据与函数,访问限定符控制权限;</li><li><strong>默认成员函数</strong>:构造(初始化)、析构(清理)、拷贝构造(复制新对象)、赋值重载(对象赋值)是基础,需区分使用场景;</li><li><strong>高级特性</strong>:初始化列表解决特殊成员初始化,static 成员实现共享资源,友元灵活访问私有成员(慎用),匿名对象简化临时操作;</li><li><strong>内存与效率</strong>:理解 this 指针、内存对齐、编译器拷贝优化,避免内存泄漏和性能问题。</li></ol>
<p>到此这篇关于C++ 类和对象全解析:从基础语法到高级特性的文章就介绍到这了,更多相关C++ 类和对象内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>C++/类与对象/默认成员函数@构造函数的用法</li><li>C++类和对象之默认成员函数的使用解读</li><li>C++类和对象之初始化列表的使用方式</li><li>c++&nbsp;rtti判断基类指针指向的真实对象类型</li><li>C++类与对象的重点知识点详细分析</li><li>C++类与对象的基础知识点详细分析</li><li>c++重载运算符时返回值为类的对象或者返回对象的引用问题</li><li>C++浅析类与对象基础点</li><li>C++类与对象深入之引用与内联函数与auto关键字及for循环详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: C++ 类和对象从基础语法到高级特性深度解析