从零开始学C++--类和对象

C++ 类和对象知识点总结

一、类的默认成员函数

1. 六大默认成员函数

  • 构造函数:初始化对象
  • 析构函数:清理资源
  • 拷贝构造函数:对象初始化对象
  • 赋值运算符重载:对象赋值对象
  • 取地址重载:普通对象和const对象
  • 移动构造/赋值(C++11):资源转移

2. 默认生成规则

  • 如果没有显式定义,编译器会自动生成默认成员函数
  • 生成的默认函数执行浅拷贝(值拷贝)
  • 如果类中有指针成员或资源管理,需要显式定义

二、构造函数

1. 特性

  • 函数名与类名相同
  • 无返回值
  • 对象实例化时自动调用
  • 可以重载
  • 支持默认参数

2. 初始化列表

cpp 复制代码
class Date {
public:
    Date(int year, int month, int day)
        : _year(year), _month(month), _day(day) {}
private:
    int _year;
    int _month;
    int _day;
};

3. 必须使用初始化列表的情况

  • 引用成员变量
  • const成员变量
  • 没有默认构造函数的类类型成员
  • 自定义类型成员(提高效率)
cpp 复制代码
class Example {
public:
    Example(int& ref, int val)
        : _ref(ref), _const(val) {}  // 引用和const必须初始化
private:
    int& _ref;           // 引用成员
    const int _const;    // const成员
};

4. explicit关键字

  • 防止隐式类型转换
  • 禁止单参数构造函数的隐式转换
cpp 复制代码
class String {
public:
    explicit String(int n) { /* 分配n个字节 */ }
};

String s1(10);      // 正确:显式调用
String s2 = 10;     // 错误:explicit禁止隐式转换

三、析构函数

1. 特性

  • 函数名:~类名
  • 无参数、无返回值
  • 对象生命周期结束时自动调用
  • 一个类只能有一个析构函数

2. 作用

  • 释放对象申请的资源
  • 自动调用,防止内存泄漏
  • 先构造的后析构(栈的后进先出)
cpp 复制代码
class Stack {
public:
    Stack(int capacity) {
        _data = new int[capacity];
    }
    ~Stack() {
        delete[] _data;  // 释放动态内存
        _data = nullptr;
    }
private:
    int* _data;
};

3. 需要显式定义的情况

  • 类中有动态内存分配(new/malloc
  • 类中有文件操作、网络连接等资源

四、拷贝构造函数

1. 定义形式

cpp 复制代码
ClassName(const ClassName& obj);

2. 特性

  • 第一个参数必须是类类型对象的引用
  • 推荐加const修饰(防止修改、支持临时对象)
  • 拷贝构造用于对象初始化对象

3. 调用场景

cpp 复制代码
void func(Date d) {}  // 值传递参数

Date d1(2024, 1, 1);
Date d2(d1);      // 场景1:对象初始化对象
Date d3 = d1;     // 场景2:对象初始化对象(本质是拷贝构造)
func(d1);         // 场景3:值传递参数

4. 深拷贝 vs 浅拷贝

浅拷贝问题示例

cpp 复制代码
class String {
public:
    String(const char* str) {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    ~String() {
        delete[] _str;
    }
private:
    char* _str;
};

String s1("hello");
String s2(s1);  // 浅拷贝:s1和s2指向同一块内存,析构时重复释放!

深拷贝解决方案

cpp 复制代码
class String {
public:
    String(const char* str) {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }

    // 深拷贝构造
    String(const String& s) {
        _str = new char[strlen(s._str) + 1];
        strcpy(_str, s._str);
    }

    ~String() {
        delete[] _str;
    }
private:
    char* _str;
};

五、赋值运算符重载

1. 定义形式

cpp 复制代码
ClassName& operator=(const ClassName& obj);

2. 特性

  • 必须是成员函数
  • 推荐返回引用(支持连续赋值)
  • 推荐加const修饰参数
  • 用于已存在对象之间的赋值

3. 经典实现

cpp 复制代码
class String {
public:
    String& operator=(const String& s) {
        if (this != &s) {  // 防止自赋值
            delete[] _str;  // 释放旧资源
            _str = new char[strlen(s._str) + 1];
            strcpy(_str, s._str);
        }
        return *this;  // 返回引用支持连续赋值
    }
private:
    char* _str;
};

4. 与拷贝构造的区别

cpp 复制代码
String s1("hello");
String s2(s1);    // 拷贝构造:s2还不存在
String s3;
s3 = s1;          // 赋值重载:s3已存在

六、const成员

1. const成员函数

cpp 复制代码
class Date {
public:
    void Print() const {  // const修饰this指针
        cout << _year << "-" << _month << "-" << _day << endl;
        // _year = 2025;  // 错误:不能修改成员变量
    }
private:
    int _year, _month, _day;
};

2. 规则

  • const成员函数不能调用非const成员函数
  • 非const成员函数可以调用const成员函数
  • const对象只能调用const成员函数
cpp 复制代码
class Test {
public:
    void Func1() { cout << "非const函数" << endl; }
    void Func2() const {
        cout << "const函数" << endl;
        // Func1();  // 错误:const函数不能调用非const函数
    }
};

int main() {
    const Test t;
    t.Func2();  // 正确:const对象调用const函数
    // t.Func1();  // 错误:const对象不能调用非const函数
}

3. 建议

  • 成员函数如果不修改成员变量,建议加const
  • 提高程序健壮性,明确函数意图

七、取地址重载

1. 普通取地址 & const取地址

cpp 复制代码
class Date {
public:
    Date* operator&() {
        cout << "普通取地址" << endl;
        return this;
    }

    const Date* operator&() const {
        cout << "const取地址" << endl;
        return this;
    }
};

int main() {
    Date d1;
    const Date d2;
    cout << &d1 << endl;  // 调用普通取地址
    cout << &d2 << endl;  // 调用const取地址
}

2. 说明

  • 一般不需要显式定义
  • 可以控制是否让外界获取对象地址

八、static成员

1. 静态成员变量

  • 属于类,不属于对象
  • 所有对象共享
  • 必须在类外初始化
  • 访问方式:ClassName::变量名对象.变量名
cpp 复制代码
class Student {
public:
    Student() { _count++; }
    static int _count;  // 静态成员变量声明
};

int Student::_count = 0;  // 类外初始化

int main() {
    Student s1, s2, s3;
    cout << Student::_count << endl;  // 输出:3
}

2. 静态成员函数

  • 没有this指针
  • 只能访问静态成员
cpp 复制代码
class Student {
public:
    static int GetCount() {  // 静态成员函数
        return _count;       // 只能访问静态成员
    }
private:
    static int _count;
};

3. 应用场景

  • 统计创建对象的个数
  • 实现单例模式
  • 共享数据

九、友元

1. 友元函数

cpp 复制代码
class Date {
    friend ostream& operator<<(ostream& out, const Date& d);
private:
    int _year, _month, _day;
};

ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "-" << d._month << "-" << d._day;
    return out;
}
  • 可以访问类的私有成员
  • 不属于类,没有this指针
  • 破坏封装,谨慎使用

2. 友元类

cpp 复制代码
class A {
    friend class B;  // B是A的友元类
private:
    int _data;
};

class B {
public:
    void Func(A& a) {
        cout << a._data << endl;  // 可以访问A的私有成员
    }
};
  • 友元类的所有成员函数都是友元函数
  • 友元关系是单向的,不可传递

十、内部类

1. 特性

  • 定义在类内部的类
  • 内部类默认是外部类的友元
  • 外部类不是内部类的友元
cpp 复制代码
class Outer {
public:
    class Inner {
    public:
        void Func(Outer& o) {
            cout << o._data << endl;  // 内部类可访问外部类私有成员
        }
    };
private:
    int _data;
};

2. 访问规则

  • 内部类可以访问外部类的所有成员
  • 外部类不能直接访问内部类的私有成员

十一、匿名对象

1. 特性

  • 生命周期只在这一行
  • 执行完立即调用析构函数
cpp 复制代码
class Test {
public:
    Test() { cout << "构造" << endl; }
    ~Test() { cout << "析构" << endl; }
};

int main() {
    Test();              // 匿名对象:构造后立即析构
    Test().Func();       // 调用成员函数
    cout << "main" << endl;
}
// 输出:构造 析构 main

十二、移动构造与移动赋值(C++11)(拓展)

1. 为什么需要移动语义

cpp 复制代码
String GetString() {
    String s("hello");
    return s;  // 传统方式:深拷贝,效率低
}
  • 传统拷贝构造需要分配新内存、复制数据
  • 移动语义可以"偷走"临时对象的资源

2. 移动构造

cpp 复制代码
class String {
public:
    // 移动构造
    String(String&& s) noexcept
        : _str(s._str) {  // 直接窃取资源
        s._str = nullptr;  // 源对象置空,防止重复释放
    }
private:
    char* _str;
};

3. 移动赋值

cpp 复制代码
String& operator=(String&& s) noexcept {
    if (this != &s) {
        delete[] _str;       // 释放自己的资源
        _str = s._str;       // 窃取对方资源
        s._str = nullptr;    // 对方置空
    }
    return *this;
}

4. std::move

cpp 复制代码
String s1("hello");
String s2(std::move(s1));  // 显式调用移动构造
// s1此时为空,不要再用

5. 规则(移动语义五法则)

如果需要定义以下任一函数,通常需要全部定义:

  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符
  • 移动构造函数
  • 移动赋值运算符

十三、=default 与 =delete(C++11)

1. =default

显式要求编译器生成默认实现:

cpp 复制代码
class Date {
public:
    Date() = default;  // 显式使用编译器生成的默认构造
    Date(int y, int m, int d);
};

2. =delete

禁止生成特定函数:

cpp 复制代码
class NonCopyable {
public:
    NonCopyable(const NonCopyable&) = delete;            // 禁止拷贝构造
    NonCopyable& operator=(const NonCopyable&) = delete; // 禁止拷贝赋值
};

NonCopyable a;
NonCopyable b(a);  // 错误:已删除

3. 应用场景

cpp 复制代码
// 禁止对象被复制(如单例模式)
class Singleton {
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

// 禁止某些参数类型的函数调用
void func(int x);
void func(double) = delete;  // 禁止double参数

func(10);    // 正确
func(3.14);  // 错误:已删除

十四、this 指针

1. 特性

  • 每个非静态成员函数都有隐式的 this 指针参数
  • this 指向调用该函数的对象
  • this 是指针,用 -> 访问成员
cpp 复制代码
class Date {
public:
    void Print() {
        cout << this->_year << endl;  // this指向调用对象
    }

    Date& GetSelf() {
        return *this;  // 返回对象本身的引用
    }
};

2. this指针的类型

cpp 复制代码
class Test {
    void func();        // this类型: Test* const
    void func() const;  // this类型: const Test* const
};
  • 普通成员函数:类名* const this(指针本身不能改)
  • const成员函数:const 类名* const this(指向内容也不能改)

3. 典型应用

cpp 复制代码
// 链式调用
class Chain {
public:
    Chain& setA(int a) { _a = a; return *this; }
    Chain& setB(int b) { _b = b; return *this; }
private:
    int _a, _b;
};

Chain c;
c.setA(1).setB(2);  // 链式调用

十五、成员变量初始化顺序

1. 重要规则

成员变量按声明顺序初始化,而非初始化列表顺序!

cpp 复制代码
class Test {
public:
    Test(int x)
        : _b(x), _a(_b) {}  // 警告:_a可能未初始化!
private:
    int _a;  // 先声明,先初始化
    int _b;  // 后声明,后初始化
};

2. 正确写法

cpp 复制代码
class Test {
public:
    Test(int x)
        : _a(x), _b(x) {}  // 不依赖其他成员
private:
    int _a;
    int _b;
};

十六、mutable 关键字(拓展)

1. 作用

允许在 const 成员函数中修改特定成员变量

cpp 复制代码
class Counter {
public:
    int GetValue() const {
        _count++;  // mutable成员可以在const函数中修改
        return _value;
    }
private:
    int _value = 0;
    mutable int _count = 0;  // 记录GetValue调用次数
};

2. 应用场景

  • 调试计数器
  • 缓存计算结果
  • 线程同步锁(mutex)

十七、空类的大小

1. 为什么空类占1字节

cpp 复制代码
class Empty {};
cout << sizeof(Empty);  // 输出:1
  • C++要求每个对象必须有唯一地址
  • 如果大小为0,数组中的对象无法区分
  • 编译器会插入1字节的占位符

2. 带成员变量的类

cpp 复制代码
class Test {
    int a;   // 4字节
    char b;  // 1字节
    // 内存对齐后,sizeof(Test) = 8
};

十八、拷贝省略(RVO/NRVO)

1. RVO(Return Value Optimization)

cpp 复制代码
String GetString() {
    return String("hello");  // 编译器优化:直接在调用处构造
}

String s = GetString();  // 可能只有一次构造,无拷贝

2. NRVO(Named RVO)

cpp 复制代码
String GetString() {
    String s("hello");
    return s;  // 编译器优化:s直接在调用处构造
}

3. C++17 保证

C++17规定在某些情况下必须省略拷贝:

cpp 复制代码
T x = T(T(T()));  // C++17保证只有一次构造

重点记忆

  1. 默认成员函数只做浅拷贝,涉及资源管理需显式定义
  2. 构造函数初始化列表是初始化成员变量的最佳方式
  3. 拷贝构造 vs 赋值重载:区分对象初始化和已存在对象赋值
  4. const成员函数提高程序健壮性,明确函数意图
  5. static成员属于类,所有对象共享
  6. 友元破坏封装,谨慎使用
  7. 深拷贝三要素:拷贝构造、赋值重载、析构函数
相关推荐
如竟没有火炬2 小时前
搜索二维矩阵
数据结构·python·算法·leetcode·矩阵
森屿~~2 小时前
PlatEMO 深度实战解析——从底层架构到 CMOPs 与 MMO 算法魔改
算法
郝学胜-神的一滴2 小时前
自动微分实战:梯度下降的迭代实现与梯度清零核心解析
人工智能·pytorch·python·深度学习·算法·机器学习
HyperAI超神经2 小时前
【TVM教程】理解 Relax 抽象层
人工智能·深度学习·学习·机器学习·gpu·tvm·vllm
一只废狗狗狗狗狗狗狗狗狗2 小时前
c语言速通复习
c语言·开发语言
daad7772 小时前
std::vector insert
算法
炽烈小老头2 小时前
【每天学习一点算法 2026/04/07】快乐数
学习·算法
程序员Ctrl喵2 小时前
Flutter 第三阶段:基础 Widget 全面指南
开发语言·javascript·flutter
用户9751470751362 小时前
Tomcat安装及配置教程(保姆级)
java