在C++中如果父类是模板类,直接调用父类成员的话,会报错找不到该变量,其根本原因是因为模板的二次编译机制导致的。
其父类的声明
template <typename T>
class SeqList:public List<T>{
protected:
T* m_array;//顺序存储空间位置
unsigned int length;//当前线性表长度
public:
bool Insert(int i, const T& e);
bool Remove(int i);
bool Set(int i, const T& e);
bool Get(int i, T& e)const;
unsigned int Length()const;
void Clear();
//数组访问方式
T& operator [](int i);
T operator [](int i)const;
//设置顺序存储空间容量
virtual unsigned int Capacity()const = 0;
};
将子类写成如下形式:
template<typename T, int N> //N为数组大小
class StaticList: public SeqList<T>{
protected:
T space[N];
public:
StaticList(){
//找不到父类的两个变量
m_array = space;
length = 0;
}
unsigned int Capacity() const{
return N;
}
};
发现编译器报错说找不到父类中的两个变量m_array,和length。
经过查询发现,模板要编译两次。
模板定义阶段(第一阶段):
只对模板中和模板参数无关的名字进行查找(无视那些有模板参数的部分)。父类是模板类,在第一次编译的时候会被无视掉。
for instance:
对于上边的例子:
StaticList继承自SeqList,在第一次编译的时候编译器会忽略掉SeqList,假装没有看到它,因此SeqList中的成员m_array和length就顺理成章地被忽略了。在StaticList中没有再次定义m_array和length,因此编译器在第一次编译的时候无法找到m_array和length直接报错了。
模板实例化阶段(第二阶段)
在第二阶段编译器主要处理带模板参数的部分,所有和模板相关的操作都在该阶段完成。
给出一个例子,也就是上边问题的解决方法。
子类:
template<typename T, int N> //N为数组大小
class StaticList: public SeqList<T>{
protected:
T space[N];
public:
StaticList(){
//父类是模板可以用Base::来调用父类成员
SeqList<T>::m_array = space;
//父类是模板可以用this来调用父类成员
this->length = 0;
}
unsigned int Capacity() const{
return N;
}
};
在这个子类中,在每个成员变量之前加入了Base::或者this指针,通过Base::或者this指针,将成员变量拖到第二阶段进行编译。this指针后边的内容都是成员变量,对于成员变量有两种情况,第一种就是在当前类中定义的变量,会在第一阶段的时候进行检查。当第一阶段无法查找到该成员的时候,就会考虑是不是父类的成员,如果父类是一个模板类,那么需要在第二阶段进行编译,因此编译器会记录下这个成员,等到第二阶段去父类中对该成员进行查找。
总结:
对于上述的问题,本质是看子类能不能在实例化之前找到其父类。如果父类是模板类就需要在实例化之后才能找到。