4

我正在研究 Michael J Laszlo 的书 ' Computation Geometry and Computer Graphics in C++ '。下面是一个模板类原型:

template <class T> class ListNode : public Node {    
    T _val;
    ListNode (T val);
    friend class List<T>;
};

template <class T> ListNode <T>::ListNode(T val)
{_val=val;};

template <class T> class List{    
  private:
    ListNode <T> *header;
    ListNode <T> *win;
    int _length;

  public:
    List(void);
    ~List(void);
    T insert(T);
    T append(T);
    List * append(List*);
    T prepend(T);
    T remove(void);
    void val(T); // overloaded function!
    T val(void);// overloaded function!
    T next(void);
    T prev(void);
    T first(void);
    T last(void);
    int length(void);
    bool isFirst(void);
    bool isLast(void);
    bool isHead(void);
};    

现在看看他定义 List 构造函数的方式:

//  constructors and destructors

template <class T> list<T>:: List(void): _length(0)
{    
    header =new ListNode<T>(NULL);
    win=header;
}

我的问题:

{...}在外部和内部分配默认长度是怎么回事?这背后有某种逻辑推理吗?

因为例如在此之前,他几乎宣布了一切之外的东西{...},我认为这只是他的风格

4

4 回答 4

17

在括号外分配默认长度而在花括号内分配默认长度是怎么回事?

这是非常普遍和可取的。该构造称为初始化列表。例如,这个

template <class T> ListNode <T>::ListNode(T val)
{_val=val;};

可以改写成这样:

template <class T> ListNode <T>::ListNode(T val)
: _val(val) {};

使用此构造指示编译器对被复制的项使用复制构造函数,而不是使用默认构造函数后跟赋值运算符。在分配原语的情况下,这无关紧要,但使用更复杂的类型初始化列表可以为您节省一些 CPU 周期。

作者没有将headerandwin赋值放入初始化列表的原因是在重要的地方强制执行特定的初始化顺序。分配 toheader必须在分配 to 之前发生win。使用初始化列表时,赋值顺序不受列表中项目顺序的控制:而是由类中相应成员的相对声明顺序控制。依赖它对读者来说非常混乱,而且太脆弱而无法留在生产环境中,因此作者正确地决定将这两个赋值移动到构造函数的主体中。

于 2013-05-24T15:48:54.507 回答
6

我阅读问题的方式,您不是在问为什么_length在初始化列表中初始化,而是在问为什么headerwin不是。

假设你有

template <class T> List<T>::List(void)
    : _length(0)
    , header(new ListNode<T>(NULL))
    , win(header)
{ }

这会做什么?这是初始化header然后将其复制到win,还是首先复制headerwin然后才设置header?您无法通过查看构造函数定义来判断。当使用普通的旧赋值时,您可以通过查看构造函数定义来判断。所以有些人(包括我自己)可能会说代码更容易阅读。

于 2013-05-24T15:54:26.870 回答
2

这是出于效率原因。该区域——在 {} 之前和 () 之后称为初始化列表。你可以在那里初始化你的变量。编译器不会默认初始化您的成员变量,而是初始化该初始化器列表中设置的变量。将其与您在 {} 中初始化变量的场景进行比较。编译器首先初始化所有成员变量,然后进入主体 {},然后重新初始化成员变量。初始化程序跳过该初始化步骤。尽可能在初始化列表中初始化。

于 2013-05-24T15:49:10.107 回答
2

首先,在初始化列表中定义值更有效。如果你不这样做,除非你有一个聪明的编译器,否则这些值将被默认初始化,并分配给。对于某些类型,这不是非常有效(尽管不在您那里的课程中)。

至于为什么他决定为这一课这样做,目前还不清楚。我只能假设这是因为他可能会在内存不足的情况下抓住新的可能抛出 - 尽管即使在那里他也可以做​​一个new(nothrow)可能更有效和更清晰的事情。

他可以尝试做的不是违反 C++ 的初始化要求的顺序——类成员按照它们在类定义中声明的顺序进行初始化,而不是按照您在构造函数初始化列表中指定它们的顺序。这可能是一个真正的痛苦,尽管大多数编译器现在会在您这样做时警告您(并且还允许您将该警告更改为错误)。在这种情况下,有可能在win之前声明过,在这种情况下,在初始化列表中的headerdoing将设置为在初始化之前的值。虽然,即使在那里,我仍然会在初始化列表中初始化标题,并且只将初始化放在代码块中。win(header)winheaderheaderwin

真正让我恼火的是他使用 (void) 参数列表。那是C-ism,看起来很丑。特别是对于默认构造函数和析构函数。

于 2013-05-24T15:58:28.917 回答