原来 C++ 这么简单:每日十题轻松学(Day 3 构造与对象行为篇 · 完整优化版)

📘 本篇聚焦 C++ 中对象生命周期的核心机制:构造函数种类、explicit 限制、资源管理规则(Rule of Five)、静态成员变量、对象内存布局等。通过十组更丰富、更实用的问题,夯实你对类的行为机制的理解。


在这里插入图片描述

🔁 Day 2 快速回顾

核心点简要记忆
运算符重载让类对象支持 + - == << 等操作
公有继承表达 is-a 关系
多态虚函数 + 父类指针/引用 + 重写
抽象类含纯虚函数,不能直接创建对象
深拷贝拷贝堆资源,避免指针悬挂或重复释放

🔟 Day 3:构造机制与对象行为十问十答(加强版)


✅ 1. C++ 中有哪些构造函数类型?各有什么用?

类型特点与用途
默认构造无参数,创建默认对象
有参构造传入参数初始化成员
拷贝构造T(const T&),复制对象
移动构造T(T&&),转移资源所有权

📌 使用建议:一旦类管理资源,建议写全四个构造函数(再加析构,即 Rule of Five)


✅ 2. 拷贝构造函数是如何被隐式调用的?陷阱有哪些?

class MyClass {
public:
    MyClass() {}
    MyClass(const MyClass &); // 拷贝构造
};

MyClass a;
MyClass b = a; // 拷贝构造

📌 触发时机:对象传参/返回、用一个对象初始化另一个

⚠️ 常见陷阱:类中有指针资源时,忘记自定义拷贝构造 → 会发生浅拷贝问题(两个对象指向同一块内存)


✅ 3. 移动构造函数的意义何在?什么时候会被调用?

MyClass a;
MyClass b = std::move(a); // 移动构造

📌 右值引用参数 T&& 被用来转移资源
📌 移动比拷贝更高效,避免资源复制,尤其对 STL 非常重要
📌 推荐加上 noexcept,以便标准库优化使用(如 vector 的 realloc)


✅ 4. explicit 构造函数解决什么问题?

class Number {
public:
    explicit Number(int x);
};

Number n1 = 5; // ❌ 错误(禁用隐式转换)
Number n2(5);  // ✅ 正确

📌 没有 explicit 时,构造函数可能被当作“类型转换函数”
📌 加上 explicit 可防止模糊的代码行为或误用


✅ 5. 构造函数初始化列表 VS 构造体赋值,有什么区别?

class A {
    const int x;
public:
    A(int val) : x(val) {}   // ✅ 推荐写法
};

📌 const 成员、引用成员必须用初始化列表
📌 效率更高:直接初始化 vs 构造后再赋值


✅ 6. 实现 Rule of Five 的类 FileHandle(带 buffer 指针)

class FileHandle {
    char* buffer;
public:
    FileHandle();
    ~FileHandle();
    FileHandle(const FileHandle&);
    FileHandle& operator=(const FileHandle&);
    FileHandle(FileHandle&&) noexcept;
    FileHandle& operator=(FileHandle&&) noexcept;
};

📌 适用于类中包含 new[]malloc 等动态资源的场景
📌 防止资源悬挂、重复释放、浅拷贝等问题


✅ 7. 设计类 Number,含 explicit 构造并对比隐式调用效果

class Number {
public:
    explicit Number(int v); // 禁止隐式 int → Number 转换
};

void print(Number n);

// print(5);    ❌ 错误,不能隐式转换
// print(Number(5)); ✅ 显式构造

📌 让你的 API 更可控,防止语义模糊
📌 推荐所有单参构造函数默认加上 explicit


✅ 8. 静态计数类 Logger:记录构造与析构次数

class Logger {
    static int constructed;
    static int destructed;
public:
    Logger();
    ~Logger();
    static void printStats();
};

int Logger::constructed = 0;
int Logger::destructed = 0;

📌 静态成员不属于对象,整个类共享一份
📌 构造/析构中自增变量可用于统计对象生命周期情况


✅ 9. 使用 sizeof() 检查类大小与成员顺序的关系

class A { char c; int x; };
class B { int x; char c; };

std::cout << sizeof(A) << " " << sizeof(B);

📌 成员顺序会引起 padding(补齐),影响内存占用
📌 char + int 可能占 8 字节,int + char 仍可能是 8,但内存访问更高效


✅ 10. new/delete 的内部机制:谁调用构造/析构?

MyClass* p = new MyClass();  // 分配 + 调构造
delete p;                    // 调析构 + 释放

📌 new 调用构造函数,delete 调用析构函数
📌 如果构造函数抛异常,内存会被释放,但不会调用析构


📚 今日总结口诀

构造有五类,规则别违背;
explicit 阻隐转,类型更清晰;
列表初始快,赋值反而慢;
静态共享数,非成员慎访问;
new/delete 成对用,vtable 影响 size;
copy 要深拷,move 省资源;
成员顺序调,对齐能省钱;
析构清资源,异常要注意。

🧠 强化练习

  1. 实现一个 BufferPool 类,管理一个堆指针数组,实现 Rule of Five;
  2. sizeof() 分析 class 成员顺序变换下的大小对比;
  3. 写一个类含 explicit 构造和 operator int(),观察何时被隐式转换调用;
  4. 拓展 Logger 类,统计“当前活动对象数量”并在每次创建/销毁时输出;

🚀 Day 4 预告

虚函数表机制(vtable)、对象切割问题(slicing)、RTTI、typeid、dynamic_cast 全解析
👀 一次性搞懂 C++ 多态运行时原理!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值