C++的构造函数是个很神奇的东东, 今天总结一下, 还是先来一个例子先:
这是上一篇博文里面的例子, 一样是出自
第2版钱能, 模版类那一章.
第一个知识点:
类或结构体的前向声明只能用来定义指针对象,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象. 就好比如说, 我想在某个类a里面, 定义一个类a自己的实例, 那是会报错滴. 以开头那个例子来说, 比如说把第6行注释放开, 或者第26行改为List<T> myself;就会得到类似于下面这样的编译报错.
-
main.cpp: In instantiation of 'Node<double>':
-
main.cpp:25: instantiated from 'List<double>'
-
main.cpp:88: instantiated from here
-
main.cpp:6: error: 'Node<T>::nodemyself' has incomplete type
-
main.cpp:5: error: declaration of 'class Node<double>'
-
main.cpp: In instantiation of 'List<double>':
-
main.cpp:88: instantiated from here
-
main.cpp:26: error: 'List<T>::myself' has incomplete type
-
main.cpp:23: error: declaration of 'class List<double>'
-
main.cpp: In instantiation of 'Node<int>':
-
main.cpp:25: instantiated from 'List<int>'
-
main.cpp:97: instantiated from here
-
main.cpp:6: error: 'Node<T>::nodemyself' has incomplete type
-
main.cpp:5: error: declaration of 'class Node<int>'
-
main.cpp: In instantiation of 'List<int>':
-
main.cpp:97: instantiated from here
-
main.cpp:26: error: 'List<T>::myself' has incomplete type
-
main.cpp:23: error: declaration of 'class List<int>
仔细看, 它们都报的错误是同一种类型的错误, 说是类型不完整. 事实上, 是由于在类自己的定义中定义自己本身, 这个时候编译会发现这个类还没有定义完全, 不知道该类或者结构的内部成员, 所以就没办法构造对象了, 因此会报错!
那么我们试着把第6行改为Node<T> *nodemyself; 第26行改为List<T> *myself; 就会发现编译可以通过. 这就说明类或者结构体的前向声明只能用来定义指针! 这个是没有问题的.
第二个知识点: 当某个类只定义了一个构造函数包含非常量引用型形参时, 系统会默认生成一个常量型引用形参. 这样的话, 编码的时候就一定要注意在定义类对象实例的时候, 添加上对应初始化参数. 还是以上面那个例子为例:
屏蔽掉第8行, 第14-16行, 让模版类只包含有一个非常量引用型形参的构造函数Node<T>::Node(T& d):c(d),next(0),pref(0) ,这样一来, 继续运行会发现有如下错误:
-
main.cpp: In constructor 'List<T>::List() [with T = double]':
-
main.cpp:88: instantiated from here
-
main.cpp:37: error: no matching function for call to 'Node<double>::Node()'
-
main.cpp:18: note: candidates are: Node<T>::Node(T&) [with T = double]
-
main.cpp:5: note: Node<double>::Node(const Node<double>&)
-
main.cpp: In constructor 'List<T>::List() [with T = int]':
-
main.cpp:97: instantiated from here
-
main.cpp:37: error: no matching function for call to 'Node<int>::Node()'
-
main.cpp:18: note: candidates are: Node<T>::Node(T&) [with T = int]
-
main.cpp:5: note: Node<int>::Node(const Node<int>&)
这里我们根据错误提示, 发现dList在初始化时会调用自己的构造函数, 因此走到第37行, 而自己的构造函数会默认调用类成员的构造函数去初始化成员, 这样一来, 就调用到了第18行的Node的构造函数, 这个时候编译器发现Node类模版只提供了一个带非常量引用型参数的构造函数, 也即:
Node(T& d); 而在List类中定义的成员函数却是这样定义的Node<T> nodemyself; 这个定义没带任何参数, 因此就没有构造函数来构造它了, 因此报错! 这也就是为什么我们屏蔽掉第8行, 第14-16行会报错的原因.
我们再证实一下, 把第25行Node<T> nodemyself; 改为Node<T> nodemyself(T); 让它强制性的符合Node的构造函数Node(T& d); 发现果然可以编译通过了.
第三个知识点:
这是由第二个知识点引申来的, 当某个类模版template class a中, 包含有由其它类模版template class b定义的成员变量时, 在定义a的构造函数的时候, 成员变量b的初始值不能放在函数名跟冒号的那个后面去初始化, 而是应该放到函数体里面(具体为什么需要这样我也没搞明白).
刚才第二点中, 我们已经把第25行Node<T> nodemyself; 改为Node<T> nodemyself(T); 让它强制性的符合Node的构造函数Node(T& d); 因为这个nodemyself毕竟是属于类模版List的成员函数, 那么我就想能否在成员函数中初始化一下它, 于是乎我把代码改为了下面这样:
(姑且称之为例2吧)
程序改成上面这样, 编译报了一个错:
-
main.cpp: In constructor 'List<T>::List(T&)':
-
main.cpp:44: error: class 'List<T>' does not have any field named 'nodemyself
它竟然说List这个模版类里面, 没有包含nodemyself这个玩意, 但是我明明定义的有. 于是乎, 我换种搞法, 把构造函数List(T &a) 写成下面这样:
-
template<typename T>
-
List<T>::List(T &a):first(0),last(0)
-
{
-
nodemyself = a;// 或者这样也行nodemyself(a);
-
}
就又编译通过了! 这里我也不知道什么原因, 没法解释. 有高手能否给个原因. 我怀疑是不是因为nodemyself本身也是个类模版定义的成员, 必须要在实例化之后才可以有真正的赋值. 所以只能赋值在函数体里面. (这里不明白, 只有先记住好了)
但是注意了, 如果我定义的是一个由类模版定义的指针, 如例2中的Node<T> *nodemyself2; 那么是可以用下面这种方式去构造的(放到冒号后面), 如下:
-
template<typename T>
-
List<T>::List(T &a):first(0),last(0),nodemyself2(&a)
-
{}
构造函数博大精深, 牵涉到模版类这种饶人的玩意的时候, 更是要注意再注意!!!
阅读(8955) | 评论(0) | 转发(0) |