简体   繁体   English

班级成员应该是指针吗?

[英]Should the member of class be a pointer?

I'm a bit confused. 我有点困惑。 If you search about the usefulness of (smart) pointers you get very different opinions. 如果你搜索(智能)指针的有用性,你会得到非常不同的意见。 Almost everybody agrees that one should use smart pointers over normal pointers, but there are also many opinions that you should not use pointers at all in modern C++. 几乎每个人都同意应该使用智能指针而不是普通指针,但也有很多意见认为你不应该在现代C ++中使用指针。

Lets consider this abstract situation (Which is similar to my problem): You have one class "Clothes" which has a member of type "Hat" 让我们考虑一下这种抽象情况(类似于我的问题):你有一个类“服装”,其成员类型为“帽子”

class Hat {
public:
    enum class HatType{
        sombrero,
        sun hat,
        helmet,
        beanie,
        cowboy_hat
    };
    // some functions
private:
    HatType type;
    // some members
}

class Clothes {
public:
    // some functions
private:
    Hat currentHat;
    // some other members of other types
}

Is there any Difference in run time if I would change Hat to Hat* (or a unique_ptr<Hat> ?). 如果我将Hat改为Hat* (或者unique_ptr<Hat> ?),运行时间是否存在差异? (In many functions of Clothes you would need to call something from Hat ) (在Clothes许多功能中你需要从Hat调用一些东西)

The reason I ask is because there are many different types of hats. 我问的原因是因为有很多不同类型的帽子。 Like sombreros, sun hats, helmets, beanies and cowboy hats. 像阔边帽,太阳帽,头盔,帽子和牛仔帽。 Right now my class hat has an enumerator which stores the hat type. 现在我的班级帽子有一个存储帽子类型的枚举器。 The hat type is only relevant in one specific function of Hat but this is the most used function. 帽子类型仅与Hat一个特定功能相关,但这是最常用的功能。

Right now I use a simple switch case in this specific function and based on the hat type the function is evaluated a bit differently. 现在我在这个特定的功能中使用一个简单的switch case ,并根据帽子类型对该功能的评估略有不同。 This worked fine but I think it would be smarter to simply make an own class for every different hat type which inherit from one single main Hat class and override the one function. 这工作正常,但我认为简单地为每个不同的帽子类型创建一个自己的类会更聪明,它继承自一个主要的Hat类并覆盖一个函数。

To do so I think I would have to change the member currentHat in Clothes to any type of pointer. 为此,我想我必须将Clothes的成员currentHat更改为任何类型的指针。 I researched if this would have any negative effects on my performance (I thought about maybe because the location of my currentHat object and my Clothes object in memory can be wide apart, but I have no Idea if this would happen and if this would have any negative effects with modern compilers). 我研究过这会对我的表现产生任何负面影响(我想过可能是因为我的currentHat对象和我的Clothes对象在内存中的位置可以分开,但我不知道是否会发生这种情况,如果这会有任何影响现代编译器的负面影响)。

During my research I often read that one should avoid pointers, this made me thinking... some of the information I found was also very old and I do not know if this is outdated. 在我的研究中,我经常读到应该避免使用指针,这让我思考......我发现的一些信息也很老,我不知道这是否已经过时了。 Is there any better way to do this? 有没有更好的方法来做到这一点?

Has anyone any experience with this kind of problem? 有没有人遇到过这种问题? Would be good to get some feedback before I spend a lot of time changing my whole project... 在花费大量时间改变整个项目之前,最好先得到一些反馈......

Side note: I took Clothes and Hats only as an examples. 旁注:我只把ClothesHats作为例子。 In my real application I have different types and I am creating many million of objects from type Clothes and I have to call functions of Clothes which will call functions of Hat many many million times, that is why I'm concerned with run time. 在我的实际应用程序中,我有不同的类型,我正在创建数百万个类型Clothes的对象,我必须调用Clothes功能,这将称呼Hat功能数百万次,这就是为什么我关心运行时间。

Edit: Maybe it is also worth noting that I have entirely avoided pointers in my application so far, mostly because I read in some books that one should try to avoid pointers :-) 编辑:也许值得注意的是,到目前为止,我已完全避免在我的应用程序中使用指针,主要是因为我在一些书中读到应该尽量避免使用指针:-)

EDIT2: Thank you for all your answers. EDIT2:谢谢你的所有答案。 Please note that part of my question was not how to do it (although the answers were very useful in that depart) but rather if the performance will suffer from this. 请注意,我的部分问题不是如何做到这一点(虽然答案非常有用)但是如果表现会受此影响。

Ok, let's break down the pointer debacle: 好吧,让我们打破指针崩溃:

Don't use explicit new / delete in C++ 不要在C ++中使用显式的new / delete

There really isn't any exception to this rule. 这个规则确实没有任何例外。 Except if you are writing a library/framework. 除非您正在编写库/框架。 Or some fancy stuff that requires placement new. 或者一些需要放置新东西的花哨的东西。 But in user code new / delete should be 100% absent. 但在用户代码中, new / delete应该100%缺席。

This brings us to my next rule: 这将我们带入下一个规则:

Don't use raw pointers to denote ownership. 不要使用原始指针来表示所有权。

This you will find often on the net as the "Don't use raw pointers" advice/rule. 你会经常在网上找到“不要使用原始指针”的建议/规则。 But in my view the problem is not with raw pointers, but with raw pointers that own an object. 但在我看来,问题不在于原始指针,而在于拥有对象的原始指针。

For ownership use smart pointers ( unique_ptr , shared_ptr ). 对于所有权使用智能指针( unique_ptrshared_ptr )。 If a pointer is not an owner of the object then it's ok if you use raw pointers. 如果指针不是对象的所有者,那么如果使用原始指针就可以了。 For instance in a tree like structure you can have unique_ptr to the children and a raw pointer to the parent. 例如,在树状结构,你可以拥有unique_ptr给孩子和一个原始指针父。

You can (arguably) also use a pointer to denote an optional value via nullptr . 您可以(可以说)使用指针通过nullptr表示可选值。

You need to use pointers or references for dynamic polymorphism 您需要使用指针或引用来进行动态多态

(Well... there are other ways, like type erasure but I won't go there in my post) If you want polymorphism then you can't use a value. (嗯......有其他方法,比如类型擦除,但我不会在我的帖子中去那里)如果你想要多态,那么你就不能使用一个值。 If you can use reference then do, but most of the time you can't. 如果你可以使用参考,那么,但大多数时候你不能。 Which often means you have to use unique_ptr . 这通常意味着您必须使用unique_ptr

Here's a breakdown of your three suggested implementations, and some more: 以下是三个建议实现的细分,以及更多:

Plain member: Hat currentHat; 普通会员: Hat currentHat;

  • Clothes uniquely owns the Hat Clothes独特拥有Hat
  • No dynamic allocation 没有动态分配
  • No polymorphism 没有多态性

Smart pointer: std::unique_ptr<Hat> currentHat; 智能指针: std::unique_ptr<Hat> currentHat;

  • Clothes still uniquely owns the Hat Clothes仍然独特拥有Hat
  • Dynamic allocation required 需要动态分配
  • Polymorphism available, ie can hold instances derived from Hat 多态可用,即可以保存从Hat派生的实例

Raw pointer: Hat *currentHat; 原始指针: Hat *currentHat;

  • Clothes does not own the Hat Clothes不拥有Hat
  • Unspecified allocation, need to ensure that the Hat outlives the Clothes 未指定的分配,需要确保HatClothes更长
  • Polymorphism still available 多态性仍然可用
  • Perfectly fine if these are your requirements. 如果这些是您的要求,那就非常好。 Fight me :) 和我战斗 :)

Unlisted contenders 未上市的竞争者

Smart pointer: std::shared_ptr<Hat> currentHat; 智能指针: std::shared_ptr<Hat> currentHat;

  • Clothes shares ownership of the Hat with zero or more other Clothes Clothes与零或多个其他Clothes共享Hat所有权
  • Same as std::unique_ptr otherwise std::unique_ptr相同

Reference: Hat &currentHat; 参考: Hat &currentHat;

  • Similar to a raw pointer 类似于原始指针
  • Is non-rebindable, thus makes the object non-assignable -- hence I don't personally like member references in the general case. 是不可重新绑定的,因此使对象不可分配 - 因此在一般情况下我个人不喜欢成员引用。

As you need different Heads, you have basically two options: 由于您需要不同的 Heads,您基本上有两个选择:

1) Using a (smart) pointer, this is well known and easy 1)使用(智能)指针,这是众所周知和容易的

2) Using std::variant 2)使用std :: variant

This is a vary different approach! 这是一种不同的方法! No longer need of a base class and ( pure ) virtual methods. 不再需要基类和(纯)虚方法。 Instead of this using the std::visit to access the current object in the tagged union . 而不是使用std::visit访问标记联合中的当前对象。 But this comes with a cost: Dispatching in std::visit can be expensive if the compiler did not hardly optimize the call table inside. 但这带来了成本:如果编译器几乎没有优化内部的调用表,那么在std :: visit中调度可能会很昂贵。 But if it can, it is simply taking the tag from the variant and use it as a index to the overloaded function from your visit call, here given as a generic lambda. 但如果可以的话,它只是从变量中获取标记并将其用作访问调用的重载函数的索引,此处作为通用lambda给出。 That is not as fast as indirect call via vtable pointer, but not more than a view instructions more ( if it is optimized!). 这不如通过vtable指针的间接调用快,但不超过视图指令更多(如果它被优化!)。

As always: How you want t go is a matter of taste. 一如既往:你想要的是一个品味问题。

Example: 例:

class Head1
{
    public:
        void Print() { std::cout << "Head1" << std::endl; }
};

class Head2
{
    public:
        void Print() { std::cout << "Head2" << std::endl; }
};

class Clothes
{
    std::variant<Head1,Head2> currentHead;

    public:

    Clothes()
    {
        currentHead = Head1();
    }

    void Do() { std::visit( [](auto& head){ head.Print();}, currentHead); }

    void SetHead( int id )
    {
        switch( id )
        {
            case 0:
                currentHead= Head1();
                break;
            case 1:
                currentHead= Head2();
                break;

            default:
                std::cerr << "Wrong id for SetHead" << std::endl;
        }
    }
};
int main()
{
    Clothes cl;
    cl.Do();
    cl.SetHead(1);
    cl.Do();
    cl.SetHead(0);
    cl.Do();
}

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

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