我想知道在访问智能指针方法之前指定智能指针的默认初始化或进行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;
}
答案 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
测试,因为这是一个明显的程序员错误,可以很容易地检测和纠正。
但是,我认为正确的答案是我们有充分的理由使用多个习语来构建和初始化,并且你应该为你的程序选择最好的方法。
通常,我将在较低级别的类中以及复杂的更高级别类中显式(无默认或无默认初始化)。如果类是中级,默认且所有权更明显(通常是因为用例有限),那么默认值可能是明智的。
通常,您只想保持一致,以避免令人惊讶的客户。您还需要了解分配默认初始化对象的复杂性。如果创建它的大而复杂,并且默认值没有意义,那么当默认构造的对象是错误的选择时,你只是浪费了大量的资源。
除了您提到的方法之外,您还可以考虑其他几个角度:
Foo
中匹配MyClass
个声明的构造函数。至少,属于MyClass
。Foo
传递给MyClass
的构造函数。Foo
传递给容器(在本例中为智能指针)到MyClass
的构造函数,以消除任何歧义,并为客户提供构造(并分享,如果是共享指针)Foo
正如他们所希望的那样。这是一种合理的做事方式还是我看不到的陷阱?
浪费分配。令人惊讶的结果。它可以限制功能。最明显,最广泛适用的问题是时间和资源消耗。
举例说明一些情况:
构造函数参数通常非常重要,通常不应从界面中删除。在某些情况下这样做肯定很好,但是随着包含对象的复杂性增加,这些便利性会引入很多限制或引入很多不必要的分配和CPU时间。
在程序中使用这两种方法都很好。使用我概述的其他方法也很好。具体来说,使用正确的问题方法是理想的 - 有多种方法可以实现理想的解决方案;你只需要确定你的程序正在尝试做什么的上下文中的内容。所有这些方法都有不同的优点和缺点 - 通常与程序操作和公开接口的上下文完美匹配。