学习C++模板---模板类作为基类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Windgs_YF/article/details/80927091
学习C++模板---模板类作为基类
使用模板类作为基类使用。
 
//
#include "stdafx.h"
#include <process.h>
#include <iostream>
template< typename T, int nMax = 5 >
class CTestVector
{
public:
     CTestVector(void)
     {
         for (int i = 0; i < nMax; i++)
         {
              m_vec[i] = 0;
         }
     }
 
     CTestVector(T vec)
     {
         for (int i = 0; i < nMax; i++)
         {
              m_vec[i] = vec;
         }
     }
 
     void print(void)
     {
         for (int i = 0; i < nMax; i++)
         {
              std::cout<< m_vec[i] << std::endl;
         }
     }
protected:
private:
     T m_vec[nMax];
};
 
//使用模板作为基类。
template< int nMax >
class CTestVectorInt : public CTestVector< int , nMax >
{
public:
    
       CTestVectorInt(void)       
     {
         cout<< "CTestVectorInt(void )" << "nMax=" << nMax << std::endl;
     }
    
     CTestVectorInt(int vec):CTestVector< int , nMax >(vec)
     {
         cout<< "CTestVectorInt(int vec) " << "nMax=" << nMax <<" vec="<< vec << std::endl;
     }
 
protected:
private:
    
};
 
 
//
//程序入口点。
//
int _tmain(int argc, _TCHAR* argv[])
{
     //使用继承模板。
//     CTestVectorInt< 2 > testVectorInt(); -------记住不要这样写当构造函数无参的时候,不会调用任何构造函数直接报错
     //CTestVectorInt< 2 > testVectorInt = CTestVectorInt< 2 >() ;--------和下面的相同
     CTestVectorInt< 2 > testVectorInt;
     testVectorInt.print();
 

  //CTestVectorInt< 2 > testVectorIntVal= CTestVectorInt< 2 >(8) ;--------和下面的相同
     CTestVectorInt< 2 > testVectorIntVal(8);
     testVectorIntVal.print();
 
     //float实例。
     CTestVector< float, 3 > testFloat( 3.14159f );
     testFloat.print();
 
 
     //double实例。
     double dA = 1.55233f;
     CTestVector< double, 6 > testDouble(dA);
     testDouble.print();
 
     //
     //暂停.
     //
     system("pause");
 
     return 0;
}

运行结果



在引入模板之后,我们面临一个新的问题,那就是如果继承自一个模板基类,是否跟继承一般的类有什么区别呢?

就是说,如果一个类继承自某个模板类,那么该类应该注意一些什么呢?其实,它与一般类的继承是很不一样的。


先举个简单的例子:

  1. template
  2. class Base
  3. {
  4. public:
  5. void printBase() {cout << "call Base printBase() ..." << endl;}
  6. };

  7. template
  8. class Derived : public Base
  9. {
  10. public:
  11. void printDerived()
  12. {
  13. cout << "call Derived printDerived() ... " << endl;
  14. printBase();
  15. }
  16. };

我们可以看到,在上述代码中,Derived类中的成员函数调用了Base类的成员函数,显然这是很合理的,因为PrintBase并没有被声明为virtual,因此,派生类直接继承其声明与实现,可是编译器却不这么认为,编译器给出的错误是:


error: there are no arguments to 'printBase' that depend on a template parameter, so a declaration of 'printBase' must be available。

这里的编译器并不承认PrintBase的存在,并且认为该函数它不认识,这与面向对象的C++出现了分歧。


关于此分歧的原因,可以由下面的例子来解释,归根结底,都是模板基类的引入。

class CompanyA
{
    public:
        void sendClearMsg() { cout << "call company A sendClear " << endl;}
        void sendEncryptedMsg() { cout << "call company A sendEncryptedMsg " << endl;}
 
};
 
class CompanyB
{
    public:
        void sendClearMsg() { cout << "call company B sendClear " << endl;}
        void sendEncryptedMsg() { cout << "call company B sendEncryptedMsg " << endl;}
 
};
 
//首先定义了两个类,用于表示各个公司自己传送信息的方式。
 
//CompanyC与上述两个公司不一样,只允许将数据加密之后再传送
 
//companyC only send encrypted Msg
class CompanyC
{
    public:
        void sendEncryptedMsg() { cout << "call company C sendEncryptedMsg " << endl;}
 
};
 
 
//定义了一个传送信息的类,对于不同的公司,封装了公司的传送信息的方式,取决于模板实参。
 
template
class MsgSender
{
    public:
        void sendClear()
        {
            cout << "call MsgSender sendClear ... " << endl;
            Company c;
            c.sendClearMsg();
        }
        void sendEncrypted()
        {
            cout << "call MsgSender sendEncrypted ... " << endl;
            Company c;
            c.sendEncryptedMsg();
        }
};
 
 
//针对CompanyC的特化版本的传送信息方式,即如果模板实参为CompanyC,那么直接调用此模板类
template<>
class MsgSender
{
    public:
        void sendClear()
        {
            cout << "call MsgSender sendClear ... " << endl;
        }
        void sendEncrypted()
        {
            cout << "call MsgSender sendEncrypted ... " << endl;
        }
};
 
//在每次将信息发出去时,记录下发出的信息。
//log the Message
template
class LogSendMsg : public MsgSender
{
public:
    void Log()
    {
        cout << "call LogSendMsg log ..." << endl;
    //    sendClear(); //错误!!因为LogSendMsg类不知道这个sendClear函数是什么?甚至不知道是哪儿来的。
        //error: there are no arguments to 'sendClear' that depend on a template parameter, so a declaration of 'sendClear' must be available|
        cout << "call LogSendMsg log end... " << endl;
 
    }
 

};


显然,在上述的LogSendMsg中,该类只有在模板形参Company被 实例化 之后才知道实际调用的是哪个Company。显然在上述的代中,CampanyC是没有定义SendClear函数的,编译器不可能一一的去预先知道这样的情况,因为用户指不定就为某家公司定义了一个特化版本,会直接导致该函数失效。


因此,编译器本着 “尽早报告错误”的可能性,在看到SendClear函数的时候,编译器不会去寻找该函数的定义,只当该函数没声明。结果就如上面的错误了。


注:这里就看出了模板基类与一般的基类的区别,一般的基类会沿着类的作用域一层一层的往上找,知道找到该函数的声明。但是模板类不会干这些无用功,是因为它知道在后续自己被 “实例化”之后,该函数可能根本就不存在(例如上述CompanyC的特化版本)。


因此,对于编译器的这种  “不进入模板基类去寻找定义” 的行为,必须由用户告诉编译器该怎么干

1  可以使用this指针,即 this->sendClear 函数。

2   使用using声明,显式地告诉编译器,我要用的是基类的sendClear函数。

3  使用作用域符号,告诉编译器这个函数是哪个类的。


值得注意的是,上述三种途径,是针对该函数一定在基类中有定义才这么干,例如上述的CompanyC并没有定义sendClear函数,那么上述做法是失效的。


总之,在使用模板基类时,对于基类中定义的名字,在派生类中必须显式地指明,因为在基类实例化之前,编译器对于基类的定义一无所知。。




定义一个类模板,注意两点:

1,类的定义前面使用关键词:template <class T>

2,函数的实现部分,在每个函数名的上一行也要加关键词template <class T>, 并且在函数名后面添加<T>,例如

  1. template <class T>
  2. ImageProcessor<T>::ImageProcessor(void)
  3. {
  4. }

3,在main.cpp文件中要同时包含模板类的.h文件和.cpp文件。

如果要从一个类模板中继承产生新的类模板注意:

1,子类的头文件中要包含父类的.h文件和.cpp文件。

2,子类的定义中,父类的名称后面要加<T>

以下是一个例子,父类是一个处理图像的通用类,子类在此继承上继承,实现对图像的下采样功能。

模板类的基类为:imageprocessor.h

  1. #pragma once

  2. template <class T>
  3. class ImageProcessor
  4. {

  5. protected:
  6. int input_size[4];
  7. T * input_data;
  8. int output_size[4];
  9. T * output_data;
  10. public:
  11. void setInputImageSize(int, int, int, int);
  12. void setInputData(T *);
  13. void setOutputImageSize(int, int, int, int);
  14. void getOutputImageSize(int []);
  15. void setOutputData(T *);
  16. bool validInputImageSize(void);
  17. bool validOutputImageSize(void);
  18. int getInputPixelPointerShift(int, int,int,int);
  19. int getOutputPixelPointerShift(int, int,int,int);
  20. T* getInputPixelPointer(int, int,int,int);
  21. T* getOutputPixelPointer(int, int,int,int);
  22. T getInputPixelValue(int, int,int,int);
  23. void setOutputPixelValue(int, int,int,int,T);
  24. ImageProcessor(void);
  25. public:
  26. ~ImageProcessor(void);
  27. };

.cpp文件为:imageprocessor.cpp

  1. #pragma once
  2. #include "StdAfx.h"
  3. #include "ImageProcessor.h"
  4. #include "windows.h"
  5. #include <iostream>
  6. using namespace std;

  7. template <class T>
  8. ImageProcessor<T>::ImageProcessor(void)
  9. {
  10. }

  11. template <class T>
  12. void ImageProcessor<T>::setInputImageSize(int h,int w,int d,int s)
  13. {
  14. }

  15. template <class T>
  16. void ImageProcessor<T>::setInputData(T* p)
  17. {
  18. }

  19. template <class T>
  20. void ImageProcessor<T>::setOutputImageSize(int h,int w,int d,int s)
  21. {
  22. }

  23. template <class T>
  24. void ImageProcessor<T>::getOutputImageSize(int size[4])
  25. {
  26. }

  27. template <class T>
  28. void ImageProcessor<T>::setOutputData(T* p)
  29. {
  30. }

  31. template <class T>
  32. bool ImageProcessor<T>::validInputImageSize(void)
  33. {
  34. }

  35. template <class T>
  36. bool ImageProcessor<T>::validOutputImageSize(void)
  37. {
  38. }

  39. template <class T>
  40. int ImageProcessor<T>::getInputPixelPointerShift(int x, int y,int z,int s)
  41. {
  42. }

  43. template <class T>
  44. int ImageProcessor<T>::getOutputPixelPointerShift(int x, int y,int z,int s)
  45. {
  46. }

  47. template <class T>
  48. T* ImageProcessor<T>::getInputPixelPointer(int x, int y,int z,int s)
  49. {
  50. }

  51. template <class T>
  52. T* ImageProcessor<T>::getOutputPixelPointer(int x, int y,int z,int s)
  53. {
  54. }

  55. template <class T>
  56. T ImageProcessor<T>::getInputPixelValue(int x, int y,int z,int s)
  57. {
  58. }

  59. template <class T>
  60. void ImageProcessor<T>::setOutputPixelValue(int x, int y,int z,int s, T value)
  61. {
  62. }

  63. template <class T>
  64. ImageProcessor<T>::~ImageProcessor(void)
  65. {
  66. }

子类的.h文件为:ipDownSampler.h

  1. #pragma once
  2. #include "imageprocessor.h"
  3. #include "imageprocessor.cpp"

  4. template <class T>
  5. class ipDownSampler :
  6. public ImageProcessor<T>
  7. {
  8. protected:
  9. double sample_rate[4];
  10. int interpolation_method;
  11. public:
  12. bool setDownSampleRate(double []);
  13. bool getDownSampledSize(void);
  14. bool validDownSampleRate(void);
  15. bool setInterpolationMethod(int);
  16. bool downSample();
  17. T getPixelValueByNearestInterpolation(double,double,double,int);
  18. T getPixelValueByBilinearInterpolation(double,double,double,int);
  19. ipDownSampler(void);
  20. public:
  21. ~ipDownSampler(void);
  22. };

其.cpp文件为:ipDownSampler.cpp

  1. #pragma once
  2. #include "StdAfx.h"
  3. #include "ipDownSampler.h"

  4. template <class T>
  5. bool ipDownSampler<T>::setDownSampleRate(double rate[4])
  6. {
  7. }

  8. template <class T>
  9. bool ipDownSampler<T>::getDownSampledSize(void)
  10. {
  11. }

  12. template <class T>
  13. bool ipDownSampler<T>::validDownSampleRate(void)
  14. {
  15. }


  16. template <class T>
  17. bool ipDownSampler<T>::setInterpolationMethod(int method)
  18. {
  19. }

  20. template <class T>
  21. bool ipDownSampler<T>::downSample()
  22. {
  23. }

  24. template <class T>
  25. T ipDownSampler<T>::getPixelValueByNearestInterpolation(double x,double y ,double z,int s)
  26. {
  27. }

  28. template <class T>
  29. T ipDownSampler<T>::getPixelValueByBilinearInterpolation(double x,double y ,double z,int s)
  30. {
  31. }

  32. template <class T>
  33. ipDownSampler<T>::ipDownSampler(void)
  34. {
  35. }

  36. template <class T>
  37. ipDownSampler<T>::~ipDownSampler(void)
  38. {
  39. }


派生类继承模板化基类成员函数, 默认是无法访问, 模板化基类的命名. 

原因模板的定制化有可能取消某些函数, 为了能在编译期检测出错误, 所以默认无法访问.


派生类访问模板化基类, 包含三种方法:

1. 调用基类函数时, 使用"this->", 指明调用的类, 是本类, 在编译时, 可以进行检查; this->size;

2. 使用using声明式, 可以把基类的函数引入派生类, 在编译时, 可以进行检查;   using Base::size;

3. 使用显示修饰(explicit qualification), 不推荐, 显示修饰会屏蔽virtual的动态绑定; Base::size;


展开阅读全文

没有更多推荐了,返回首页