C++中的trivial和non-trivial构造/析构/拷贝/赋值函数及POD类型

文章详细介绍了C++中的trivial和non-trivial概念,主要针对类的构造函数、复制构造函数、赋值函数和析构函数。POD类型是指具有C兼容性的简单结构,具备平凡的构造和析构等特性,允许直接内存操作以提高效率。C++11中,POD类型进一步细分为平凡的和标准布局的。了解这些概念对于理解和优化C++代码的性能至关重要。
摘要由CSDN通过智能技术生成

在侯捷的《STL源码剖析》里提到trivial和non-trivial及POD类型,相关知识整理如下。

trivial意思是无意义,这个trivial和non-trivial是对类的四种函数来说的:

  • 构造函数(ctor)

  • 复制构造函数(copy)

  • 赋值函数(assignment)

  • 析构函数(dtor)

简单来说,“平凡”意味着这些特殊的成员函数用很朴素的方式完成它们的工作。而”很朴素的方式“这个说法对不同的函数有不同的意义。

对默认构造函数和析构函数来说,“平凡”意味着什么也不做。

对拷贝构造函数和拷贝赋值函数来说,“平凡”意味着只做简单的内存拷贝

如果至少满足下面3条里的一条:

  • 显式(explict)定义了这四种函数。

  • 类里有非静态非POD的数据成员。

  • 有基类。

那么上面的四种函数是non-trivial函数,比如叫non-trivial ctor、non-trivial copy…,也就是说有意义的函数,里面由一些必要的操作,比如类成员的初始化、释放内存等。

那个POD意思是Plain Old Data,也就是C++的内建类型或传统的C结构体类型。POD类型必然有trivial ctor/dtor/copy/assignment四种函数。

// 整个T是POD类型
class T
{
    // 没有显式定义ctor/dtor/copy/assignemt所以都是trivial
    int a; // POD类型
};

// 整个T1是非POD类型
class T1
{
    T1() // 显式定义了构造函数,所以是non-trivial ctor
    {
    }
    // 没有显式定义dtor/copy/assignemt所以都是trivial
    int a;         // POD类型
    std::string b; // 非POD类型
};

对POD类型的一些补充:

POD 是英文中 Plain Old Data 的缩写,翻译过来就是 普通的旧数据 。POD 在 C++ 中是非常重要的一个概念,通常用于说明一个类型的属性,尤其是用户自定义类型的属性。

POD 属性在 C++11 中往往又是构建其他 C++ 概念的基础,事实上,在 C++11 标准中,POD 出现的概率相当高。因此学习 C++,尤其是在 C++11 中,了解 POD 的概念是非常必要的。

Plain :表示是个普通的类型

Old :体现了其与 C 的兼容性,支持标准 C 函数

在 C++11 中将 POD 划分为两个基本概念的合集,即∶平凡的(trivial) 和标准布局的(standard layout )。

一个平凡的类或者结构体应该符合以下几点要求:

1、拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor)。

平凡的默认构造函数就是说构造函数什么都不干。

  • 通常情况下,不定义类的构造函数,编译器就会为我们生成一个平凡的默认构造函数。

// 使用默认的构造函数
class Test {};
  • 一旦定义了构造函数,即使构造函数不包含参数,函数体里也没有任何的代码,那么该构造函数也不再是"平凡"的。

class Test1 
{
    Test1();    // 程序猿定义的构造函数, 非默认构造
};

关于析构函数也和上面列举的构造函数类似,一旦被定义就不平凡了。但是这也并非无药可救,使用=default 关键字可以显式地声明默认的构造函数,从而使得类型恢复“平凡化”。

2、拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial move constructor)。

  • 平凡的拷贝构造函数基本上等同于使用 memcpy 进行类型的构造。

  • 同平凡的默认构造函数一样,不声明拷贝构造函数的话,编译器会帮程序员自动地生成。

  • 可以显式地使用 = default 声明默认拷贝构造函数。

  • 而平凡移动构造函数跟平凡的拷贝构造函数类似,只不过是用于移动语义。

3、拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator)。

这基本上与平凡的拷贝构造函数和平凡的移动构造函数类似。

4、不包含虚函数以及虚基类。

  • 类中使用 virtual 关键字修饰的函数 叫做虚函数

class Base 
{
public:
    Base() {}
    virtual void print() {}
};
  • 虚基类是在创建子类的时候在继承的基类前加 virtual 关键字修饰

语法: 
class 派生类名:virtual 继承方式 基类名

示例代码:

class Base 
{
public:
    Base() {}
};
// 子类Child,虚基类:Base
class Child : virtual public Base
{
    Child() {}
};

那这有什么用处呢?

如果这个类是trivial ctor/dtor/copy/assignment函数,我们对这个类进行构造、析构、拷贝和赋值的时候可以采用最有效率的方法,不调用无所事事的那些ctor/dtor等,而直接采用内存操作如malloc()、memcpy()等提高性能,这也是SGI STL内部干的事情。

比如STL的copy算法最基本的想法是这样的:

// 非POD重载指针数值
template << / span > class T > void copy(T *source, T *destination, int n, __false_type)
{
    // 省略异常处理
    for (; n > 0; n--, source++, destination++)
    {
        // 调用source的拷贝构造函数
        constructor(source, *destination);
    }
}

// POD重载指针数值
template << / span > class T > void copy(T *source, T *destination, int n, __false_type)
{
    // 省略异常处理
    memmove(source, destination, n);
}

当然实际的copy比这个复杂多了,有非常多的特化等,这个只是其中一方面而已。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/object.hpp:664:34: error: ‘void* memcpy(void*, const void*, size_t)’ copying an object of non-trivial type ‘struct msgpack::v2::object’ from an array of ‘const msgpack_object’ {aka ‘const struct msgpack_object’} [-Werror=class-memaccess] std::memcpy(&o, &v, sizeof(v)); ^ In file included from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object_fwd.hpp:17, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/adaptor/adaptor_base_decl.hpp:14, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/adaptor/adaptor_base_decl.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/adaptor/adaptor_base.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/object_decl.hpp:16, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object_decl.hpp:14, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack.hpp:10, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/example/cpp03/stream.cpp:10: /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v2/object_fwd.hpp:23:8: note: ‘struct msgpack::v2::object’ declared here struct object : v1::object { ^~~~~~ cc1plus: all warnings being treated as errors make[2]: *** [example/cpp03/CMakeFiles/stream.dir/build.make:63:example/cpp03/CMakeFiles/stream.dir/stream.cpp.o] 错误 1 make[2]: 离开目录“/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master” make[1]: *** [CMakeFiles/Makefile2:415:example/cpp03/CMakeFiles/stream.dir/all] 错误 2 make[1]: 离开目录“/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master”
07-20
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值