Chinaunix首页 | 论坛 | 博客
  • 博客访问: 389113
  • 博文数量: 80
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 560
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-10 08:38
文章分类
文章存档

2016年(32)

2015年(48)

我的朋友

分类: LINUX

2016-01-06 14:58:07

C++的构造函数是个很神奇的东东, 今天总结一下, 还是先来一个例子先:


点击(此处)折叠或打开

  1. #include <iostream>
  2. using namespace std;

  3. template<typename T>
  4. class Node{
  5.         //Node<T> nodemyself;
  6.     public:
  7.         Node();
  8.         Node(T& d);
  9.         T c;
  10.         Node<T> *next,*pref;
  11.     };
  12.     
  13. template<typename T>
  14. Node<T>::Node()
  15. {}
  16. template<typename T>
  17. Node<T>::Node(T& d):c(d),next(0),pref(0)
  18. {}

  19.     
  20. template<typename T>
  21. class List{
  22.     Node<T> *first, *last;
  23.     Node<T> nodemyself;
  24.     List<T> *myself;
  25.     public:
  26.         List();
  27.         void add(T& c);
  28.         void remove(T& c);
  29.         Node<T>* find(T& c);
  30.         void print();
  31.         ~List();
  32.     };
  33.     
  34. template<typename T>
  35. List<T>::List():first(0),last(0)
  36. {}

  37. template<typename T>
  38. void List<T>::add(T& n)
  39. {
  40.     Node<T>* p = new Node<T>(n);
  41.     p->next = first;
  42.     first = p;
  43.     (last?p->next->pref:last) = p;
  44. }

  45. template<typename T>
  46. void List<T>::remove(T& n)
  47. {
  48.     Node<T> *p = find(n);
  49.     if(!p) return;
  50.     (p->next?p->next->pref:last) = p->pref;
  51.     (p->pref?p->pref->next:first) = p->next;
  52.     delete p;
  53. }

  54. template<typename T>
  55. Node<T>* List<T>::find(T& n)
  56. {
  57.     for(Node<T> *p = first; p;p=p->next)
  58.     {
  59.         if(p->c == n) return p;
  60.     }
  61.     return 0;
  62. }

  63. template<typename T>
  64. List<T>::~List()
  65. {
  66.     for(Node<T> *p; p = first;delete p)
  67.         first = first->next;
  68. }

  69. template<typename T>
  70. void List<T>::print()
  71. {
  72.     for(Node<T>* p=first;p;p=p->next)
  73.         cout<< p->c<<" ";
  74.     cout<< "\n";
  75. }

  76. int main()
  77. {
  78.     //double a1 = 1.1;    
  79.     //Node<double> nodemyself(a1);
  80.     List<double> dList;
  81.     double a = 3.6;
  82.     double b = 5.8;
  83.     dList.add(a);
  84.     dList.add(b);
  85.     dList.print();
  86.     
  87.     int c=5;
  88.     int d=8;
  89.     List<int> iList;
  90.     iList.add(c);
  91.     iList.add(d);
  92.     iList.print();
  93. }

这是上一篇博文里面的例子, 一样是出自第2版钱能, 模版类那一章.

第一个知识点: 
        
类或结构体的前向声明只能用来定义指针对象,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象.  就好比如说, 我想在某个类a里面, 定义一个类a自己的实例, 那是会报错滴. 以开头那个例子来说,  比如说把第6行注释放开, 或者第26行改为List<T> myself;就会得到类似于下面这样的编译报错.

点击(此处)折叠或打开

  1. main.cpp: In instantiation of 'Node<double>':
  2. main.cpp:25: instantiated from 'List<double>'
  3. main.cpp:88: instantiated from here
  4. main.cpp:6: error: 'Node<T>::nodemyself' has incomplete type
  5. main.cpp:5: error: declaration of 'class Node<double>'
  6. main.cpp: In instantiation of 'List<double>':
  7. main.cpp:88: instantiated from here
  8. main.cpp:26: error: 'List<T>::myself' has incomplete type
  9. main.cpp:23: error: declaration of 'class List<double>'
  10. main.cpp: In instantiation of 'Node<int>':
  11. main.cpp:25: instantiated from 'List<int>'
  12. main.cpp:97: instantiated from here
  13. main.cpp:6: error: 'Node<T>::nodemyself' has incomplete type
  14. main.cpp:5: error: declaration of 'class Node<int>'
  15. main.cpp: In instantiation of 'List<int>':
  16. main.cpp:97: instantiated from here
  17. main.cpp:26: error: 'List<T>::myself' has incomplete type
  18. 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)  ,这样一来, 继续运行会发现有如下错误:

点击(此处)折叠或打开

  1. main.cpp: In constructor 'List<T>::List() [with T = double]':
  2. main.cpp:88: instantiated from here
  3. main.cpp:37: error: no matching function for call to 'Node<double>::Node()'
  4. main.cpp:18: note: candidates are: Node<T>::Node(T&) [with T = double]
  5. main.cpp:5: note:                  Node<double>::Node(const Node<double>&)
  6. main.cpp: In constructor 'List<T>::List() [with T = int]':
  7. main.cpp:97: instantiated from here
  8. main.cpp:37: error: no matching function for call to 'Node<int>::Node()'
  9. main.cpp:18: note: candidates are: Node<T>::Node(T&) [with T = int]
  10. 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吧)

点击(此处)折叠或打开

  1. #include <iostream>
  2. using namespace std;

  3. template<typename T>
  4. class Node{
  5.         Node<T> *nodemyself;
  6.     public:
  7. //    Node();
  8.         Node(T& d);
  9.         T c;
  10.         Node<T> *next,*pref;
  11.     };
  12.     
  13. //template<typename T>
  14. //Node<T>::Node()
  15. //{}
  16. template<typename T>
  17. Node<T>::Node(T& d):c(d),next(0),pref(0)
  18. {}

  19.     
  20. template<typename T>
  21. class List{
  22.     private:
  23.     Node<T> *first, *last;
  24.     Node<T> nodemyself(T);
  25.     Node<T> *nodemyself2;
  26.     List<T> *myself;
  27.     public:
  28.         List();
  29.         List(T &a);
  30.         void add(T& c);
  31.         void remove(T& c);
  32.         Node<T>* find(T& c);
  33.         void print();
  34.         ~List();
  35.     };
  36.     
  37. template<typename T>
  38. List<T>::List():first(0),last(0)
  39. {}

  40. template<typename T>
  41. List<T>::List(T &a):first(0),last(0),nodemyself(a)
  42. {
  43.     //nodemyself = a;
  44. }

  45. template<typename T>
  46. void List<T>::add(T& n)
  47. {
  48.     Node<T>* p = new Node<T>(n);
  49.     p->next = first;
  50.     first = p;
  51.     (last?p->next->pref:last) = p;
  52. }

  53. template<typename T>
  54. void List<T>::remove(T& n)
  55. {
  56.     Node<T> *p = find(n);
  57.     if(!p) return;
  58.     (p->next?p->next->pref:last) = p->pref;
  59.     (p->pref?p->pref->next:first) = p->next;
  60.     delete p;
  61. }

  62. template<typename T>
  63. Node<T>* List<T>::find(T& n)
  64. {
  65.     for(Node<T> *p = first; p;p=p->next)
  66.     {
  67.         if(p->c == n) return p;
  68.     }
  69.     return 0;
  70. }

  71. template<typename T>
  72. List<T>::~List()
  73. {
  74.     for(Node<T> *p; p = first;delete p)
  75.         first = first->next;
  76. }

  77. template<typename T>
  78. void List<T>::print()
  79. {
  80.     for(Node<T>* p=first;p;p=p->next)
  81.         cout<< p->c<<" ";
  82.     cout<< "\n";
  83. }

  84. int main()
  85. {
  86.     //double a1 = 1.1;    
  87.     //Node<double> nodemyself(a1);
  88.     List<double> dList;
  89.     double a = 3.6;
  90.     double b = 5.8;
  91.     dList.add(a);
  92.     dList.add(b);
  93.     dList.print();
  94.     
  95.     int c=5;
  96.     int d=8;
  97.     List<int> iList;
  98.     iList.add(c);
  99.     iList.add(d);
  100.     iList.print();
  101. }
    程序改成上面这样, 编译报了一个错:

点击(此处)折叠或打开

  1. main.cpp: In constructor 'List<T>::List(T&)':
  2. main.cpp:44: error: class 'List<T>' does not have any field named 'nodemyself
它竟然说List这个模版类里面, 没有包含nodemyself这个玩意, 但是我明明定义的有.  于是乎, 我换种搞法, 把构造函数List(&a) 写成下面这样:

点击(此处)折叠或打开

  1. template<typename T>
  2. List<T>::List(T &a):first(0),last(0)
  3. {
  4.     nodemyself = a;// 或者这样也行nodemyself(a);
  5. }
就又编译通过了!  这里我也不知道什么原因, 没法解释. 有高手能否给个原因.  我怀疑是不是因为nodemyself本身也是个类模版定义的成员, 必须要在实例化之后才可以有真正的赋值. 所以只能赋值在函数体里面. (这里不明白, 只有先记住好了)

但是注意了, 如果我定义的是一个由类模版定义的指针, 如例2中的Node<T> *nodemyself2; 那么是可以用下面这种方式去构造的(放到冒号后面), 如下:

点击(此处)折叠或打开

  1. template<typename T>
  2. List<T>::List(T &a):first(0),last(0),nodemyself2(&a)
  3. {}

      构造函数博大精深, 牵涉到模版类这种饶人的玩意的时候, 更是要注意再注意!!!


    
              













    
                           




                                    



















                                                                           










阅读(8955) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~