默认初始化或检查空

时间:2012-06-14 17:37:49

标签: c++ null smart-pointers default-constructor

我想知道在访问智能指针方法之前指定智能指针的默认初始化或进行NULL值检查更好吗? < / p>

目前,我一直在使用以下方法,以避免在increment()指针上调用NULL这是一种合理的做事方式,还是有一个我不明白的陷阱?

注意:我们使用自定义智能指针类,并且我没有在当前配置上使用Boost库来测试编译此代码。这应该编译,但是YMMV。

example.h文件

#include <boost/shared_ptr.hpp>

class Foo
{
public:
  Foo() : mFoo(0) {}
  Foo(int rawValue) : mFoo(rawValue) {}

  void  increment() { mFoo++; }

private:
  int mFoo;
};

typedef boost::shared_ptr<Foo> FooSP;

class MyClass
{
public:
  MyClass() : mFoo(new Foo()) {}

  FooSP       foo() { return mFoo; }
  void        setFoo(FooSP newFoo) { mFoo = newFoo; }

private:
  FooSP mFoo;
};

Main.cpp的

#include <Example.h>

int main()
{
  MyClass temp;                    // Default-constructed
  temp.foo()->increment();         // Increment Foo's member integer
                                   // Before: mFoo = 0
                                   // After:  mFoo = 1
  FooSP tempFoo = new Foo(10);     // Create a Foo with a default size
  temp.setFoo(FooSP(new Foo(10))); // Explicitly set the FooSP member
  temp.foo()->increment();         // Increment the new FooSP
                                   // Before: mFoo = 10
                                   // After:  mFoo = 11
  return 0;
}

2 个答案:

答案 0 :(得分:3)

如果您使用智能指针作为指针类型的一般替换,则无法远离检查null。这是因为使用带有默认构造函数的智能指针定义的类可能允许使用其默认构造函数创建智能指针。动态创建一个新对象只是为了填充指针,直到你可以设置它似乎是浪费资源。

shared_ptr的构造函数是显式的,因此tempFoo的初始化将无法编译。如果您想保存一行代码,可以避免像这样声明临时代码:

temp.setFoo(FooSP(new Foo(10)));

您还可以声明setFoo的方法采用常量引用,以避免在接收参数时操纵引用计数。

void setFoo(const FooSP &newFoo) { mFoo = newFoo; }

或者在参数实例上使用swap

void setFoo(FooSP newFoo) { std::swap(mFoo, newFoo); }

如果我被要求按照您提议的内容实现某些内容,我将创建一个Foo的静态实例作为空版本,然后让increment方法抛出一个异常,如果它是null版本。

class Foo
{
public:
  static Foo Null;
  //...
  void  increment() {
      if (this == &Null) throw Null;
      mFoo++;
  }
  //...
};

struct DeleteFoo {
    void operator () (Foo *t) const {
        if (t != &Foo::Null) delete t;
    }
};

class MyClass
{
public:
  MyClass() : mFoo(&Foo::Null, DeleteFoo()) {}
  //...
};

请注意FooSP的自定义删除工具,以便正确处理Foo::Null

答案 1 :(得分:2)

  

最好在访问智能指针方法之前指定智能指针的默认初始化或进行NULL值检查吗?

没有正确的答案适用于每个案例(更快)。 如果我不得不犯错误,我会在没有默认初始化的情况下进行NULL测试,因为这是一个明显的程序员错误,可以很容易地检测和纠正。

但是,我认为正确的答案是我们有充分的理由使用多个习语来构建和初始化,并且你应该为你的程序选择最好的方法

通常,我将在较低级别的类中以及复杂的更高级别类中显式(无默认或无默认初始化)。如果类是中级,默认且所有权更明显(通常是因为用例有限),那么默认值可能是明智的。

通常,您只想保持一致,以避免令人惊讶的客户。您还需要了解分配默认初始化对象的复杂性。如果创建它的大而复杂,并且默认值没有意义,那么当默认构造的对象是错误的选择时,你只是浪费了大量的资源。

  • a)在没有意义的情况下不要应用默认值。默认应该是显而易见的。
  • b)避免浪费分配。

除了您提到的方法之外,您还可以考虑其他几个角度:

  • Foo中匹配MyClass个声明的构造函数。至少,属于MyClass
  • 的那些
  • 如果可复制且有效复制,请将Foo传递给MyClass的构造函数。
  • Foo传递给容器(在本例中为智能指针)到MyClass的构造函数,以消除任何歧义,并为客户提供构造(并分享,如果是共享指针)Foo正如他们所希望的那样。
  

这是一种合理的做事方式还是我看不到的陷阱?

浪费分配。令人惊讶的结果。它可以限制功能。最明显,最广泛适用的问题是时间和资源消耗。

举例说明一些情况:

  • 说Foo每次构建时都会读取1MB文件。当需要构造参数且默认值不是正确选项时,必须再次读取该文件。无辜的默认值会使所需的磁盘翻倍。
  • 在另一种情况下,省略的构造参数可以是另一个大的或复杂的共享指针。如果缺席,Foo可以创建自己的 - 当资源可以/应该已经共享时。

构造函数参数通常非常重要,通常不应从界面中删除。在某些情况下这样做肯定很好,但是随着包含对象的复杂性增加,这些便利性会引入很多限制或引入很多不必要的分配和CPU时间。

在程序中使用这两种方法都很好。使用我概述的其他方法也很好。具体来说,使用正确的问题方法是理想的 - 有多种方法可以实现理想的解决方案;你只需要确定你的程序正在尝试做什么的上下文中的内容。所有这些方法都有不同的优点和缺点 - 通常与程序操作和公开接口的上下文完美匹配。