C++学习笔记——初始化列表、创建和实例化对象、new 关键字、隐式构造与 explicit 关键字、运算符与运算符重载

目录

[1. 初始化列表](#1. 初始化列表)

[1.1 基本语法](#1.1 基本语法)

[1.2 为什么使用初始化列表?](#1.2 为什么使用初始化列表?)

[1.3 初始化顺序](#1.3 初始化顺序)

[2. 创建和实例化对象](#2. 创建和实例化对象)

[2.1 栈上分配(自动存储期)](#2.1 栈上分配(自动存储期))

[2.2 堆上分配(动态存储期)](#2.2 堆上分配(动态存储期))

[2.3 栈 vs 堆:Cherno 的建议](#2.3 栈 vs 堆:Cherno 的建议)

[3. new 关键字](#3. new 关键字)

[3.1 new 的作用](#3.1 new 的作用)

[3.2 new 与 malloc 的区别](#3.2 new 与 malloc 的区别)

[3.3 数组形式的 new 与 delete](#3.3 数组形式的 new 与 delete)

[3.4 定位 new(placement new)](#3.4 定位 new(placement new))

[4. 隐式构造与 explicit 关键字](#4. 隐式构造与 explicit 关键字)

[4.1 隐式构造(隐式类型转换)](#4.1 隐式构造(隐式类型转换))

[4.2 explicit 关键字](#4.2 explicit 关键字)

[5. 运算符与运算符重载](#5. 运算符与运算符重载)

[5.1 基本概念](#5.1 基本概念)

[5.2 成员函数 vs 全局函数](#5.2 成员函数 vs 全局函数)

[5.3 常用运算符重载示例](#5.3 常用运算符重载示例)


1. 初始化列表

1.1 基本语法

初始化列表位于构造函数参数列表之后、函数体之前,以冒号 : 开头,多个成员用逗号分隔。

cpp 复制代码
class Entity {
private:
    std::string m_Name;
    int m_Age;
public:
    Entity(const std::string& name, int age)
        : m_Name(name), m_Age(age)   // 初始化列表
    {
        // 构造函数体
    }
};

1.2 为什么使用初始化列表?

  • 效率:成员变量在进入构造函数体之前已经完成初始化(直接调用其构造函数),而不是先默认构造再在函数体内赋值。

  • 必须使用初始化列表的场景

    • const 成员变量

    • 引用成员变量

    • 没有默认构造函数的成员对象

    • 基类构造函数(尤其是需要带参数的基类)

cpp 复制代码
class Base {
public:
    Base(int x) {}
};

class Derived : public Base {
private:
    const int m_ConstVal;
    int& m_Ref;
public:
    Derived(int a, int& ref)
        : Base(a), m_ConstVal(a), m_Ref(ref)  // 必须使用初始化列表
    {
    }
};

1.3 初始化顺序

初始化顺序 按照成员在类中声明的顺序,而不是初始化列表的顺序。为避免依赖未初始化的成员,应保证顺序一致。

cpp 复制代码
class Example {
    int m_A;
    int m_B;
public:
    Example() : m_B(2), m_A(m_B) {} // 危险:m_A 先初始化,此时 m_B 尚未初始化
};

2. 创建和实例化对象

2.1 栈上分配(自动存储期)

  • 语法Entity e;Entity e("name");

  • 生命周期:在作用域结束时自动调用析构函数并释放内存。

  • 优点:极快(仅栈指针移动),无需手动管理内存。

  • 缺点:生命周期局限于作用域;对象大小受栈空间限制(通常几 MB)。

cpp 复制代码
void func() {
    Entity e;          // 栈上创建,自动调用构造函数
    e.DoSomething();
} // 自动调用析构函数

2.2 堆上分配(动态存储期)

  • 语法Entity* e = new Entity();new Entity("name")

  • 生命周期 :需显式调用 delete e; 释放,否则内存泄漏。

  • 优点:对象生命周期可控,可跨作用域存在;适合大型对象或需要动态创建的场景。

  • 缺点:速度慢(需堆分配、内存管理开销),容易造成内存泄漏或悬空指针。

cpp 复制代码
Entity* createEntity() {
    Entity* e = new Entity("Cherno");
    return e;          // 对象在堆上,函数返回后仍然存在
}

void use() {
    Entity* e = createEntity();
    e->DoSomething();
    delete e;          // 必须手动释放
}

2.3 栈 vs 堆:Cherno 的建议

  • 优先使用栈分配,除非确实需要动态生命周期(如多态、对象数量不确定、跨函数传递所有权)。

  • 如果需要动态分配,现代 C++ 应使用 智能指针std::unique_ptrstd::shared_ptr)代替裸指针,避免手动 delete

3. new 关键字

3.1 new 的作用

  • 分配内存 :调用 operator new(类似 malloc)分配足够的内存。

  • 调用构造函数:在该内存上构造对象。

  • 返回指向该对象的指针。

3.2 new 与 malloc 的区别

特性 new malloc
内存来源 自由存储区(通常是堆)
是否调用构造函数
返回类型 类型安全的指针(T* void*
内存大小 自动计算(sizeof(T) 需手动指定字节数
失败处理 抛出 std::bad_alloc 异常 返回 nullptr

3.3 数组形式的 new 与 delete

cpp 复制代码
int* arr = new int[10];   // 分配 10 个 int 的数组
delete[] arr;              // 必须使用 delete[],保证调用每个元素的析构函数

3.4 定位 new(placement new)

可以在已分配的内存上构造对象,常用于自定义内存管理。

cpp 复制代码
char* buffer = new char[sizeof(Entity)];
Entity* e = new (buffer) Entity("Cherno");   // 在 buffer 位置构造对象
e->~Entity();                                 // 手动调用析构
delete[] buffer;

4. 隐式构造与 explicit 关键字

4.1 隐式构造(隐式类型转换)

  • 隐式构造:当构造函数只接受一个参数(或除第一个参数外都有默认值),编译器允许在需要该类型对象的地方使用该参数类型,自动调用构造函数进行隐式转换。

  • 隐式转换可能造成意料之外的对象构造,降低代码可读性,甚至引发隐蔽的错误。

4.2 explicit 关键字

  • 作用 :在构造函数前加 explicit,禁止该构造函数参与隐式类型转换。
cpp 复制代码
class Object {
private:
	std::string m_Name;
	int m_Age;
public:
	explicit Object(const const std::string name)  :
		m_Name(name), m_Age(-1) {}

	Object(int age)
		: m_Name("Unknown"), m_Age(age) {}
};

void PrintObj(const Object& obj) {
	
}

int main() {
	//隐式构造
	PrintObj(1);
	Object obj = 22;

	//explicit 会禁止隐式构造
	//PrintObj(std::string("Cheer"));会报错
	//Object obj = std::string("Cheer");
	std::cin.get();
}

5. 运算符与运算符重载

5.1 基本概念

运算符重载允许为用户定义的类型提供自定义的运算符行为。可以重载的运算符包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、下标运算符、函数调用运算符等。某些运算符不能重载(如 ::..*?:sizeof 等)。

5.2 成员函数 vs 全局函数

  • 成员函数 :左操作数是当前对象(this)。

  • 全局函数:可灵活支持隐式类型转换,通常需要声明为友元。

cpp 复制代码
class Vector2 {
public:
    float x, y;
    Vector2 operator+(const Vector2& other) const {   // 成员函数
        return {x + other.x, y + other.y};
    }
};

// 全局函数版本(通常用于对称性,如实现 operator<<)
Vector2 operator-(const Vector2& a, const Vector2& b) {
    return {a.x - b.x, a.y - b.y};
}

5.3 常用运算符重载示例

赋值运算符 =

cpp 复制代码
class String {
    char* m_Data;
public:
    String& operator=(const String& other) {
        if (this != &other) {        // 自赋值检查
            delete[] m_Data;
            m_Data = new char[strlen(other.m_Data) + 1];
            strcpy(m_Data, other.m_Data);
        }
        return *this;
    }
};

流插入 << 与流提取 >>

cpp 复制代码
class Point {
    int m_X, m_Y;
public:
    friend std::ostream& operator<<(std::ostream& os, const Point& p);
};

std::ostream& operator<<(std::ostream& os, const Point& p) {
    os << "(" << p.m_X << ", " << p.m_Y << ")";
    return os;
}

下标运算符 []

cpp 复制代码
class Array {
    int data[100];
public:
    int& operator[](int index) { return data[index]; }
    const int& operator[](int index) const { return data[index]; }
};

函数调用运算符 ()

cpp 复制代码
class Multiply {
public:
    int operator()(int a, int b) const { return a * b; }
};
Multiply mul;
int result = mul(3, 4);   // 12
相关推荐
南境十里·墨染春水2 小时前
C++笔记 类模板(面向对象)
开发语言·c++·笔记
小陈phd2 小时前
多模态大模型学习笔记(二十八)—— 基于Qwen多模态大模型的城市道路积水智能检测助手实战
笔记·学习
煜磊2 小时前
C/C++语言部署安装_C/C++Api学习
开发语言·c++
低频电磁之道3 小时前
C++ 源码文本格式规范
开发语言·c++
共享家95273 小时前
C++核心之多线程
开发语言·c++
南境十里·墨染春水3 小时前
C++ 笔记 function 函数包装器模板
开发语言·c++·笔记
研來如此3 小时前
tinyxml2 常用读取接口对照表
xml·c++·tinyxml2
MC皮蛋侠客3 小时前
C++中使用Redis指南:基于redis-plus-plus库
开发语言·c++·redis
婷婷_1723 小时前
【PCIe验证每日学习·Day24】PCIe 原子操作、锁定事务与总线仲裁机制
学习·程序人生·芯片·原子操作·总线仲裁·pcie 验证·pcie学习