类的成员应该是指针吗?

时间:2018-03-16 09:36:13

标签: c++ class oop pointers

我有点困惑。如果你搜索(智能)指针的有用性,你会得到非常不同的意见。 几乎每个人都同意应该使用智能指针而不是普通指针,但也有很多意见认为你不应该在现代C ++中使用指针。

让我们考虑一下这种抽象情况(与我的问题类似): 你有一个班级"衣服"它的成员类型为" 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
}

如果我将Hat更改为Hat*(或unique_ptr<Hat>?),运行时间是否有任何差异?)。 (在Clothes的许多功能中,您需要从Hat

调用某些内容

我问的原因是因为有许多不同类型的帽子。 像阔边帽,太阳帽,头盔,帽子和牛仔帽。现在我的班级帽子有一个存储帽子类型的枚举器。 帽子类型仅与Hat的一个特定函数相关,但这是最常用的函数。

现在我在这个特定的函数中使用了一个简单的switch case,并且基于hat类型,函数的评估有点不同。这工作正常但我认为简单地为每个不同的帽子类型创建一个自己的类会更聪明,它继承自一个主要的Hat类并覆盖一个函数。

为此,我想我必须将currentHat中的成员Clothes更改为任何类型的指针。我研究过这会对我的表现产生任何负面影响(我想过可能是因为我的currentHat对象和我的Clothes对象在内存中的位置可能相距很远,但我不知道如果这样会发生,如果这会对现代编译器产生任何负面影响)。

在我的研究中,我经常读到应该避免使用指针,这让我思考......我发现的一些信息也很老,我不知道这是否已经过时了。有没有更好的方法呢?

有没有人遇到过这种问题?在我花费大量时间改变整个项目之前获得一些反馈会很好......

旁注:我仅以ClothesHats为例。在我的实际应用程序中,我有不同的类型,我正在创建类型为Clothes的数百万个对象,我必须调用Clothes的函数,它将调用Hat的函数数百万次,这就是为什么我关注运行时间。

编辑:也许值得注意的是,到目前为止,我已完全避免使用我的应用程序中的指针,主要是因为我在一些书中读到应该尽量避免使用指针: - )

EDIT2:谢谢你的所有答案。请注意,我的部分问题不是如何做到这一点(虽然答案非常有用)但是如果表现会受此影响。

3 个答案:

答案 0 :(得分:5)

好的,让我们分解指针崩溃:

不要在C ++中使用显式new / delete

此规则确实没有任何例外。除非您正在编写库/框架。或者一些需要放置新东西的花哨的东西。但在用户代码new / delete中应该100%缺席。

这使我们进入下一个规则:

不要使用原始指针来表示所有权。

你会经常在网上找到“不要使用原始指针&#34;建议/规则。但在我看来,问题不在于原始指针,而在于拥有对象的原始指针。

对于所有权使用智能指针(unique_ptrshared_ptr)。如果指针不是对象的所有者,那么如果使用原始指针,它就没问题。例如,在树状结构中,您可以对子项具有unique_ptr,并为父项指定原始指针。

您可以(可以说)使用指针通过nullptr表示可选值。

您需要使用动态多态性的指针或引用

(嗯......还有其他的方式,比如类型擦除,但是我不会在我的帖子中去那里)如果你想要多态,那么你就不能使用一个值。如果你可以使用参考,那么,但大多数时候你不能。这通常意味着您必须使用unique_ptr

答案 1 :(得分:3)

这是您的三个建议实施的细分,还有一些:

普通会员:Hat currentHat;

  • Clothes唯一拥有Hat
  • 无动态分配
  • 无多态性

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

  • Clothes仍然唯一拥有Hat
  • 需要动态分配
  • 可用的多态性,即可以保存从Hat
  • 派生的实例

原始指针:Hat *currentHat;

  • Clothes不拥有Hat
  • 未指定的分配,需要确保Hat超过Clothes
  • 多态性仍然可用
  • 如果这些是您的要求,那就非常好。打我:)

未列出的竞争者

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

  • Clothes分享Hat的所有权,其中包含零个或多个其他Clothes
  • std::unique_ptr相同,否则

参考:Hat &currentHat;

  • 与原始指针类似
  • 不可重新绑定,因此使得该对象不可转让 - 因此,在一般情况下,我个人不喜欢会员参考。

答案 2 :(得分:1)

由于你需要不同的 Heads,你基本上有两个选择:

1)使用(智能)指针,这是众所周知的,简单

2)使用std :: variant

这是一种不同的方法!不再需要基类和(纯)虚方法。而不是使用std::visit来访问标记的联合中的当前对象。但这带来了成本:如果编译器几乎没有优化内部的调用表,那么在std :: visit中调度可能会很昂贵。但如果可以的话,它只是从变量中获取标记并将其用作访问调用的重载函数的索引,此处作为通用lambda给出。这不如通过vtable指针的间接调用快,但不超过一个视图指令更多(如果它被优化!)。

一如既往:你想要的是一个品味问题。

示例:

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();
}