简体   繁体   English

我可以将final关键字应用于C ++ 11中的POD(标准布局)结构吗?我是不是该?

[英]Can I apply the final keyword to a POD (standard-layout) struct in C++11 ? Should I?

In a C++ project that is full of objects (with proper behaviors) and relatively few of non-object-oriented structs (consisting of only data fields and no methods), I would like to prevent the accidental misuse of these structs, in which one might try to create a class inheriting from it. 在一个充满对象(具有适当行为)和相对较少的非面向对象结构(仅由数据字段和无方法组成)的C ++项目中,我想防止意外滥用这些结构,其中一个可能会尝试创建一个继承自它的类。

According to my understanding, because these "POD" (plain old data) structs do not have a virtual destructor, it is not possible to properly delete a derived class object (if one is allowed to create it) via a pointer of the POD type. 根据我的理解,因为这些“POD”(普通旧数据)结构没有虚拟析构函数,所以无法通过POD类型的指针正确删除派生类对象(如果允许创建它) 。

This seems to make a good use case for the C++11 "final" keyword, which marks a class or struct as non-inheritable. 这似乎是C ++ 11“final”关键字的一个很好的用例,它将类或结构标记为不可继承。

However, I wonder if the "final" keyword causes the struct to become non-POD? 但是,我想知道“final”关键字是否会导致结构变为非POD?

I suspect that the standards documentation may have addressed this issue, but I am not intelligent enough to sift through the very long documentations to find out. 我怀疑标准文档可能已经解决了这个问题,但我没有足够的智慧来筛选很长的文档来查找。 Any useful pointers would be welcome. 任何有用的指针都是受欢迎的。

Note: I am not interested in merely knowing that it passes the compilation of some compiler vendors. 注意:我只是不知道它通过了一些编译器供应商的编译。 Passing compilation do not guarantee: 通过汇编不保证:

  • Whether the compiled code will execute correctly in all situations (especially when the technique is applied in a larger, more complex project), 编译的代码是否会在所有情况下正确执行(特别是当技术应用于更大,更复杂的项目时),
  • Whether the C++ standards body intends this to be used in this way. C ++标准组织是否打算以这种方式使用它。
#include <iostream>
using namespace std;

struct Pod final
{
    int a;
    int b;
    int c;
};

#if 0
class FailsBecauseCannotDeriveFromFinalAnything : public Pod
{
};
#endif

class ContainsSomethingFinalAsMember
{
public:
    ContainsSomethingFinalAsMember() : pod() {}
private:
    Pod pod;
};

int main() 
{
    std::cout << std::is_pod < Pod > :: value << std::endl;
    return 0;
}

According to my understanding, because these "POD" (plain old data) structs do not have a virtual destructor, it is not possible to properly delete a derived class object (if one is allowed to create it) via a pointer of the POD type. 根据我的理解,因为这些“POD”(普通旧数据)结构没有虚拟析构函数,所以无法通过POD类型的指针正确删除派生类对象(如果允许创建它) 。

It's not possible given a raw pointer, but it is possible given a smart pointer object, such as std::shared_ptr or a std::unique_ptr with an appropriate deleter. 给定一个原始指针是不可能的,但是可以给出一个智能指针对象,例如std::shared_ptr或带有适当删除器的std::unique_ptr

Since smart pointers were standardized, there are few excuses to continue following the poor practice of using the delete operator manually. 由于智能指针是标准化的,因此没有理由继续遵循手动使用delete操作符的不良做法。 Certainly, classes shouldn't be designed around compatibility with the delete operator. 当然,不应该围绕与delete运算符的兼容性来设计类。 Each class interface should be designed with its particular uses in mind. 每个类接口的设计都应考虑其特定用途。

No, it's not a good practice to make every class either final or polymorphic. 不,让每个课程都是final或多态的并不是一个好习惯。

However, I wonder if the "final" keyword causes the struct to become non-POD? 但是,我想知道“final”关键字是否会导致结构变为非POD?

No, it's still POD. 不,它仍然是POD。 The requirements for POD are standard layout (in turn requiring no base class, no access specifiers between members, no virtual anything) and trivial special member functions (copy/move constructor, assignment operator, destructor). POD的要求是标准布局(反过来不需要基类,成员之间没有访问说明符,没有虚拟任何东西)和普通的特殊成员函数(复制/移动构造函数,赋值运算符,析构函数)。

However, the purpose of POD-ness is to allow you to use memcpy instead of properly constructing objects, which good C++ code would avoid doing. 但是,POD-ness的目的是允许你使用memcpy而不是正确构造对象,这是好的C ++代码可以避免的。

Marking a class final does not change its POD status. 标记类final并不会改变其POD状态。 Quoting C++11: 引用C ++ 11:

9 [class]: 9 [班级]:

6 A trivially copyable class is a class that: 6一个简单的可复制类是一个类:

  • has no non-trivial copy constructors (12.8), 没有非平凡的拷贝构造函数(12.8),
  • has no non-trivial move constructors (12.8), 没有非平凡的移动构造函数(12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8), 没有非平凡的副本赋值运算符(13.5.3,12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and 没有非平凡的移动赋值运算符(13.5.3,12.8),和
  • has a trivial destructor (12.4). 有一个简单的析构函数(12.4)。

A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable. 一个普通的类是一个具有普通默认构造函数(12.1)并且可以轻易复制的类。 [ Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. [ 注意:特别是,一个简单的可复制或普通的类没有虚函数或虚基类。 —end note ] - 尾注 ]

7 A standard-layout class is a class that: 7 标准布局类是一个类:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference, 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • has no virtual functions (10.3) and no virtual base classes (10.1), 没有虚函数(10.3),也没有虚基类(10.1),
  • has the same access control (Clause 11) for all non-static data members, 对所有非静态数据成员具有相同的访问控制(第11条),
  • has no non-standard-layout base classes, 没有非标准布局基类,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
  • has no base classes of the same type as the first non-static data member. 没有与第一个非静态数据成员相同类型的基类。

8 A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class . 标准布局结构是使用类键 struct类键 class定义的标准布局class ... ...

10 A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). 10 POD结构是一个非联合类,它既是普通类,也是标准布局类,并且没有非POD结构类型的非静态数据成员,非POD联合(或此类型的数组)。 ... ...

12.1 [class.ctor] 12.1 [class.ctor]

5 ... A default constructor is trivial if it is not user-provided and if: 5 ...如果默认构造函数不是用户提供的,则默认构造函数是微不足道的,如果:

  • its class has no virtual functions (10.3) and no virtual base classes (10.1), and 它的类没有虚函数(10.3),也没有虚基类(10.1),和
  • no non-static data member of its class has a brace-or-equal-initializer, and 没有类的非静态数据成员有一个大括号或等于初始化器,和
  • all the direct base classes of its class have trivial default constructors, and 它的所有直接基类都有简单的默认构造函数,和
  • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor. 对于类类的所有非静态数据成员(或其数组),每个这样的类都有一个普通的默认构造函数。

Otherwise, the default constructor is non-trivial. 否则,默认构造函数是非平凡的。

12.4 [class.dtor] 12.4 [class.dtor]

5 ... A destructor is trivial if it is not user-provided and if: 5 ...如果不是用户提供的,析构函数是微不足道的,如果:

  • the destructor is not virtual, 析构函数不是虚拟的,
  • all of the direct base classes of its class have trivial destructors, and 它的所有直接基类都有琐碎的析构函数
  • for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor. 对于类类的所有非静态数据成员(或其数组),每个这样的类都有一个简单的析构函数。

Otherwise, the destructor is non-trivial. 否则,析构函数是非平凡的。

12.8 [class.copy] 12.8 [class.copy]

12 A copy/move constructor for class X is trivial if it is not user-provided and if — class X has no virtual functions (10.3) and no virtual base classes (10.1), and — the constructor selected to copy/move each direct base class subobject is trivial, and — for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial; 12,用于A类复制/移动的构造X是微不足道的,如果它不是用户提供的,并且如果-类X没有虚函数(10.3),并且没有虚基类(10.1),以及-选择要复制的构造/移动每个直接基类子对象是微不足道的,并且 - 对于类类型(或其数组)的X每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;

otherwise the copy/move constructor is non-trivial. 否则复制/移动构造函数是非平凡的。

25 A copy/move assignment operator for class X is trivial if it is not user-provided and if 25如果不是用户提供的,则X类的复制/移动赋值运算符是微不足道的

  • class X has no virtual functions (10.3) and no virtual base classes (10.1), and class X没有虚函数(10.3),没有虚基类(10.1),和
  • the assignment operator selected to copy/move each direct base class subobject is trivial, and 选择复制/移动每个直接基类子对象的赋值运算符是微不足道的,并且
  • for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial; 对于类型(或其数组)的X每个非静态数据成员,选择复制/移动该成员的赋值运算符是微不足道的;

otherwise the copy/move assignment operator is non-trivial. 否则复制/移动赋值运算符是非常重要的。

As you see, the definition of a POD struct does not take into account whether the class has the class-virt-specifier final . 如您所见,POD结构的定义没有考虑该类是否具有class-virt-specifier final

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM