C++平凡类型和非平凡类型异同

平凡类型(Trivial Type)

定义

在 C++ 中,一个类型如果满足以下几个条件,就被称为平凡类型:

  1. 平凡的默认构造函数:没有用户自定义的默认构造函数,或者默认构造函数是使用 = default 显式默认化的,并且所有基类和非静态数据成员都有平凡的默认构造函数。
  2. 平凡的拷贝构造函数:没有用户自定义的拷贝构造函数,或者拷贝构造函数是使用 = default 显式默认化的,同时所有基类和非静态数据成员都有平凡的拷贝构造函数。
  3. 平凡的移动构造函数:没有用户自定义的移动构造函数,或者移动构造函数是使用 = default 显式默认化的,且所有基类和非静态数据成员都有平凡的移动构造函数。
  4. 平凡的拷贝赋值运算符:没有用户自定义的拷贝赋值运算符,或者拷贝赋值运算符是使用 = default 显式默认化的,并且所有基类和非静态数据成员都有平凡的拷贝赋值运算符。
  5. 平凡的移动赋值运算符:没有用户自定义的移动赋值运算符,或者移动赋值运算符是使用 = default 显式默认化的,所有基类和非静态数据成员都有平凡的移动赋值运算符。
  6. 平凡的析构函数:没有用户自定义的析构函数,或者析构函数是使用 = default 显式默认化的,并且析构函数不是 virtual 的。
  7. 非虚:不能有虚函数或者虚基类
示例代码
#include <iostream>

// 平凡类型示例
class TrivialType {
    int value; // 普通数据成员
    // 没有用户自定义的构造、拷贝、移动、赋值和析构函数
    // 编译器会自动生成平凡版本
};

int main() {
    TrivialType obj1; // 调用平凡默认构造函数
    TrivialType obj2(obj1); // 调用平凡拷贝构造函数
    obj2 = obj1; // 调用平凡拷贝赋值运算符
    TrivialType obj3(std::move(obj1)); // 调用平凡移动构造函数
    obj3 = std::move(obj2); // 调用平凡移动赋值运算符
    return 0;
}
特点
  • 简单的内存操作:由于平凡类型的对象构造、拷贝、移动和析构操作都是简单的位操作,所以可以使用 memcpymemset 等函数进行高效的内存操作。
  • 可 POD 化:平凡类型在很多情况下可以被看作是 POD(Plain Old Data)类型,即可以和 C 语言的数据类型兼容,方便进行数据的存储和传输。

非平凡类型(Non - Trivial Type)

定义

不满足平凡类型定义的类型就是非平凡类型。也就是说,只要一个类型存在用户自定义的构造函数、拷贝构造函数、移动构造函数、拷贝赋值运算符、移动赋值运算符或者析构函数,或者析构函数是 virtual 的,标准容器库,涉及到动态内存管理的,那么它就是非平凡类型。

示例代码
#include <iostream>

// 非平凡类型示例
class NonTrivialType {
public:
    int* data;
    // 自定义默认构造函数
    NonTrivialType() : data(new int(0)) {
        std::cout << "Default constructor called" << std::endl;
    }
    // 自定义拷贝构造函数
    NonTrivialType(const NonTrivialType& other) : data(new int(*other.data)) {
        std::cout << "Copy constructor called" << std::endl;
    }
    // 自定义析构函数
    ~NonTrivialType() {
        delete data;
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    NonTrivialType obj1; // 调用自定义默认构造函数
    NonTrivialType obj2(obj1); // 调用自定义拷贝构造函数
    return 0;
}
特点
  • 复杂的资源管理:非平凡类型通常需要进行复杂的资源管理,例如动态内存分配、文件句柄管理等。在对象的构造、拷贝、移动和析构过程中,需要确保资源的正确分配和释放,避免内存泄漏等问题。
  • 不适合简单内存操作:由于非平凡类型的对象构造和析构可能包含复杂的逻辑,不能简单地使用 memcpymemset 等函数进行内存操作,否则可能会导致资源管理错误。

区分平凡类型和非平凡类型的原因

性能优化
  • 平凡类型:对于平凡类型,编译器可以进行更高效的优化。例如,在进行对象的拷贝时,可以直接使用 memcpy 进行位拷贝,这样的操作速度非常快。
  • 非平凡类型:非平凡类型的对象拷贝和移动需要调用相应的构造函数和赋值运算符,这些操作可能包含复杂的逻辑,性能相对较低。通过区分平凡类型和非平凡类型,程序员可以根据具体情况选择合适的操作方式,提高程序的性能。
资源管理
  • 平凡类型:平凡类型不需要进行复杂的资源管理,因为它们的构造、拷贝、移动和析构操作都是简单的位操作。这使得平凡类型在处理简单数据时更加方便和安全。
  • 非平凡类型:非平凡类型通常需要进行复杂的资源管理,例如动态内存分配、文件句柄管理等。区分平凡类型和非平凡类型可以帮助程序员更好地管理资源,避免资源泄漏和其他错误。
兼容性
  • 平凡类型:平凡类型可以和 C 语言的数据类型兼容,方便进行数据的存储和传输。在与 C 语言代码进行交互时,平凡类型可以直接使用,而不需要进行额外的转换。
  • 非平凡类型:非平凡类型通常不与 C 语言的数据类型兼容,因为它们的构造和析构可能包含复杂的逻辑。在与 C 语言代码进行交互时,需要进行额外的处理。
模板编程和泛型算法

在模板编程和泛型算法中,区分平凡类型和非平凡类型可以使代码更加通用和高效。例如,标准库中的一些算法会根据类型是否为平凡类型来选择不同的实现方式,以提高性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值