C++类和对象(下):从初始化列表到编译器优化

​​​​​​​​​​​​​​C++类和对象(上)https://blog.youkuaiyun.com/wz_290793/article/details/149486147?spm=1001.2014.3001.5501
C++类和对象(中)https://blog.youkuaiyun.com/wz_290793/article/details/149505547?spm=1001.2014.3001.5501

目录

C++类和对象(下):从初始化列表到编译器优化

一、构造函数初始化列表:更高效的成员初始化方式

二、类型转换:显式与隐式的艺术

隐式类型转换

显式类型转换

三、static成员:类的共享资源

static成员变量

static成员函数

四、友元:打破封装的特权

友元函数

友元类

五、内部类:类中的类

六、匿名对象:即用即弃的临时工

七、编译器优化:看不见的性能提升

常见的拷贝优化

八、综合实例:一个完整的类设计

九、总结与最佳实践


C++类和对象(下):从初始化列表到编译器优化

一、构造函数初始化列表:更高效的成员初始化方式

在C++中,构造函数初始化成员变量有两种主要方式:函数体内赋值和初始化列表。初始化列表以冒号开头,后跟以逗号分隔的成员初始化列表,每个成员变量后跟括号中的初始值或表达式。

class Example {
public:
    Example(int x, int y) : m_x(x), m_y(y) {}  // 初始化列表
private:
    int m_x;
    int m_y;
};

​初始化列表的核心规则​​:

  1. 引用成员、const成员和没有默认构造的类类型成员​​必须​​使用初始化列表
  2. 每个成员在初始化列表中只能出现一次
  3. C++11支持成员变量声明时给缺省值,作为初始化列表的备选
  4. 初始化顺序由成员声明顺序决定,而非初始化列表中的顺序

​为什么优先使用初始化列表​​?

  • 效率更高:直接初始化而非先默认构造再赋值
  • 某些类型只能通过初始化列表初始化
  • 保持代码一致性,减少错误

二、类型转换:显式与隐式的艺术

C++支持丰富的类型转换机制,但不当使用可能导致难以发现的错误。

隐式类型转换

class MyInt {
public:
    MyInt(int i) : value(i) {}  // 转换构造函数
    operator int() { return value; }  // 转换函数
private:
    int value;
};

MyInt mi = 42;      // 隐式转换:int → MyInt
int i = mi;         // 隐式转换:MyInt → int

显式类型转换

使用explicit关键字禁止隐式转换:

class SafeInt {
public:
    explicit SafeInt(int i) : value(i) {}
    explicit operator int() { return value; }
private:
    int value;
};

// SafeInt si = 42;  // 错误:不能隐式转换
SafeInt si(42);      // 正确:显式构造
int j = static_cast<int>(si);  // 必须显式转换

​最佳实践​​:

  • 对可能引起歧义的构造函数使用explicit
  • 优先使用C++风格的类型转换(static_cast等)
  • 避免C风格的强制类型转换

三、static成员:类的共享资源

static成员属于类而非对象,是所有类实例共享的资源。

static成员变量

class Counter {
public:
    Counter() { ++count; }
    ~Counter() { --count; }
    static int getCount() { return count; }
private:
    static int count;  // 声明
};

int Counter::count = 0;  // 定义并初始化

static成员函数

  • 没有this指针
  • 只能访问static成员
  • 可通过类名或对象调用

​应用场景​​:

  • 类级别的计数器
  • 共享资源配置
  • 工具函数(不依赖对象状态)

四、友元:打破封装的特权

友元机制允许特定函数或类访问私有成员,是一把双刃剑。

友元函数

class Secret {
private:
    int secret_code;
    friend void reveal(const Secret&);  // 友元声明
};

void reveal(const Secret& s) {
    cout << s.secret_code;  // 可以访问私有成员
}

友元类

class Box {
private:
    int width;
    friend class BoxFriend;  // 友元类声明
};

class BoxFriend {
public:
    static void peek(const Box& b) {
        cout << b.width;  // 可以访问私有成员
    }
};

​友元使用原则​​:

  1. 最小化友元关系
  2. 文档化友元的设计理由
  3. 考虑替代方案(如公有接口)
  4. 友元关系不可传递(A→B→C ≠ A→C)

五、内部类:类中的类

内部类是定义在另一个类内部的类,具有特殊访问权限。

class Outer {
private:
    int outer_data;
public:
    class Inner {  // 默认是Outer的友元
    public:
        void accessOuter(Outer& o) {
            cout << o.outer_data;  // 可以访问私有成员
        }
    };
};

​内部类特点​​:

  • 默认是外部类的友元
  • 受外部类访问控制影响
  • 适合实现仅被外部类使用的组件
  • 不占用外部类对象空间

六、匿名对象:即用即弃的临时工

匿名对象是没有名称的临时对象,生命周期仅限当前表达式。

class Temp {
public:
    Temp() { cout << "Created"; }
    ~Temp() { cout << "Destroyed"; }
    void work() { cout << "Working"; }
};

int main() {
    Temp().work();  // 输出:Created Working Destroyed
    // 匿名对象在此已销毁
}

​应用场景​​:

  1. 临时测试对象功能
  2. 链式调用中间结果
  3. 简化只使用一次的对象创建

七、编译器优化:看不见的性能提升

现代编译器会对对象拷贝进行各种优化,减少不必要的开销。

常见的拷贝优化

  1. ​返回值优化(RVO)​​:消除返回值的临时对象
  2. ​命名返回值优化(NRVO)​​:消除命名返回值的拷贝
  3. ​拷贝省略​​:消除临时对象的构造和析构
class Heavy {
public:
    Heavy() { cout << "Construct"; }
    Heavy(const Heavy&) { cout << "Copy"; }
};

Heavy create() {
    return Heavy();  // 可能被优化为直接构造返回值
}

int main() {
    Heavy h = create();  // 可能只输出"Construct"
}

​优化控制​​:

  • -fno-elide-constructors:禁用拷贝优化(调试用)
  • 不同编译器优化策略可能不同

八、综合实例:一个完整的类设计

#include <iostream>
#include <string>

class Student {
public:
    // explicit防止隐式转换
    explicit Student(const std::string& name) 
        : name_(name), id_(++total_students) {}
    
    // 静态成员函数
    static int getTotalStudents() { return total_students; }
    
    // 友元函数
    friend void printStudentDetails(const Student&);
    
    // 内部类
    class Score {
    public:
        Score(int math, int physics) 
            : math_(math), physics_(physics) {}
        void print() const {
            std::cout << "Math: " << math_ 
                      << ", Physics: " << physics_;
        }
    private:
        int math_;
        int physics_;
    };
    
    // 添加成绩(使用匿名对象)
    void addScore(int math, int physics) {
        score_ = Score(math, physics);  // 匿名Score对象
    }
    
    // 显示成绩
    void showScore() const {
        score_.print();
    }
    
private:
    std::string name_;
    int id_;
    Score score_{0, 0};  // 使用C++11的成员初始化
    static int total_students;  // 静态成员计数
};

int Student::total_students = 0;  // 静态成员定义

// 友元函数实现
void printStudentDetails(const Student& s) {
    std::cout << "Student: " << s.name_ 
              << " (ID: " << s.id_ << ")\n";
    s.showScore();
}

int main() {
    Student alice("Alice");
    alice.addScore(90, 85);
    
    printStudentDetails(alice);
    
    std::cout << "\nTotal students: " 
              << Student::getTotalStudents();
    
    return 0;
}

九、总结与最佳实践

  1. ​初始化列表​​:优先使用,特别是对于const、引用和没有默认构造的成员
  2. ​类型安全​​:合理使用explicit防止意外的隐式转换
  3. ​static成员​​:用于类级别的数据和功能
  4. ​友元机制​​:谨慎使用,避免过度破坏封装
  5. ​内部类​​:组织相关代码,增强封装性
  6. ​匿名对象​​:简化一次性使用的对象创建
  7. ​信任编译器​​:但了解其优化行为有助于写出更高效的代码

通过掌握这些高级特性,你可以设计出更安全、更高效、更易维护的C++类,为构建复杂系统打下坚实基础。
 

当然,c++类与对象的征程才刚刚开始,但学习是循序渐进的过程,本栏目将c++类与对象剩下的内容归为进阶c++,等后续初阶段知识文章发布完善,再更新c++进阶内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值