C++:如何将成员指针初始化为null?

34
我有一个类看起来像这样:

class Foo
{
public:
    Foo();
    virtual ~Foo();

private:
    Odp* bar;
};

我希望将 bar 初始化为 NULL。这是最佳方式吗?

Foo::Foo() : bar(NULL)
{
}

还有,析构函数必须是虚函数吗?(如果是这样的话,那构造函数也必须是虚函数吗?)


1
可以说更好的方式是 Foo::Foo() : bar() {} (注意,没有 NULL)。最好的方式是不使用指针。 - GManNickG
2
为什么这样更好?NULL是隐式的吗? - Nick Heiner
2
“为什么这样更好?” 你不需要包含头文件来定义 NULL(它只是 0)。 “NULL 是隐式的吗?” 是的。 bar() 是一个值初始化;对于指针,它将其初始化为 null。 - Mike Seymour
稍微少一些情况,只有在以下情况下才需要将它设置为虚拟的:
  • 有人继承了你的类
  • 有人使用指向你的类的指针来删除该对象,即使这样也只会在派生类存在非平凡析构函数时引起问题。因为最后一个条件很难确定,一般的建议是:如果有人从你那里派生出来,你应该使用虚拟析构函数。在此上下文中,“派生”是指 Java 的“extends”,而不是“implements”。
- dascandy
7个回答

39

我希望将bar初始化为NULL,这是最好的方法吗?

这是正确的方法,所以是的。

另外,析构函数是否必须是虚拟的?

不是必须的。只有当您从Foo类继承并将使用Foo指针删除这些派生类时(尽管作为一般准则,如果有其他虚成员,则应该是虚拟的)才需要将析构函数设置为虚拟的。

(如果是这样,那么构造函数也必须是虚拟的吗?)

不需要。构造函数既不需要不能是虚拟的。


+1 很好的简明回答。我在构造函数的主体中初始化非常量成员。这使得代码阅读更加清晰(关于成员限定符类型的)。 - Poni
16
@Poni:不,你不需要初始化它们,你需要重新赋值。所有成员变量都会在构造函数体开始时被初始化。这就是为什么通常认为在那里“初始化”东西是不好的做法。当然,对于原始类型,你不会失去任何东西,但并非所有类型都是原始类型,最好保持一致性。随着你使用它们的次数增多并熟悉了语法,你会清楚如何使用它们。 - GManNickG
MyClass::MyClass() : m_const_var(VALUE) { this->m_non_const_var = OTHER_VALUE } <<<< 我没有完全明白你说的,GMan,但我想表达的是这个意思。所以,你是说这种方法是错误的吗?你是说我们应该像对待m_const_var一样,在构造函数成员初始化区域包含m_non_const_var的初始化吗? - Poni
3
@Poni: 我知道你的意思,我想指出m_non_const_var应该在初始化列表中初始化。当你写=OTHER_VALUE时,你是在赋值而不是初始化。初始化列表是用来初始化的。 :) 如果你的代码只是不一致,或者有某些原因不能初始化,那么它会让我感到困惑。构造函数体应该是运行任何需要初始化的代码而不是成员变量的地方。 - GManNickG
1
析构函数只有在通过基类指针进行多态删除时才需要是虚拟的,否则将其设置为受保护和非虚拟即可。请参见例如Virtuality - Georg Fritzsche

10
  1. 是的,初始化列表是最佳选择。

  2. 也许。如果你打算在类中有任何其他虚函数,或者打算从该类进行继承(尽管通常这些事情是一起完成的),那么析构函数应该是虚拟的。

  3. 不可以。在C++中不可能有虚拟构造函数。(这样的东西究竟意味着什么?)

你提出的问题的性质使我认为你并不真正理解virtual关键字的作用及其用途,而只是复制了某个地方或教程上的内容。最好理解你正在编写的代码的所有目的。以下可能是一个开始的地方:http://www.parashift.com/c++-faq-lite/virtual-functions.html


仅供参考,对于“这种事情甚至意味着什么?”的回应:有些人可能会将“虚构造函数”这个术语用于工厂模式(http://www2.research.att.com/~bs/bs_faq2.html#virtual-ctor)。 - Michael Burr

9
四种不同的方式可供选择,哪一种最好取决于您。
Foo::Foo() : bar() // value initialization
{
}

Foo::Foo() : bar(0) // direct null pointer constant
{
}

Foo::Foo() : bar(NULL) // null pointer constant by macro
{
}

Foo::Foo() : bar(nullptr) // pointer literal of type std::nullptr_t
{
}

现在的代码是 Foo::Foo(): bar(nullptr){} - nurettin

4

3
另一个你可能想考虑的选项是使用智能指针类(例如boost::scoped_ptrboost::shared_ptr或C++0x的unique_ptr),而不是原始指针。智能指针的构造函数将确保它被初始化为类似于NULL的东西,如果你不需要其他显式初始化。智能指针还将确保所指向的对象被销毁。
你只需要决定什么样的智能指针策略适合该项并相应选择(即使auto_ptr可能比原始指针更好,只要你意识到各种陷阱)。

2

1,是的。

2,只有当你希望某人能够从你的类派生并使用指向基类的指针时才需要这样做 - 但无论如何都要使dtor虚拟化。

3,不,你不能拥有虚拟构造函数(或者我想说所有构造函数都是虚拟的吧?)


4
“所有构造函数都是虚函数”- 不是的,决定调用哪个构造函数是在编译时进行的。 “虚函数”意味着在运行时做出决策。 - Mike Seymour

0

虚函数用于在运行时确定类(在基类和派生类中都定义)的哪个函数必须被调用。但是当对象被创建时,编译器知道应该调用哪个构造函数。例如,当创建基类对象时,将调用基类构造函数,创建派生类对象时也是如此。因此,使构造函数成为虚函数没有任何意义。但是,一旦基类对象指针指向派生类对象,然后调用析构函数,编译器会混淆需要调用哪个析构函数(基类或派生类),这只能通过查找表vtable来解决,因此析构函数需要是虚函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接