Шаблоны: переменные-члены родительского класса не видны в унаследованном классе
У меня есть следующие 4 файла:
-
arrayListType.h
: объявить и определить класс arrayListType
как шаблон
-
unorderedArrayListType.h
: Унаследовано от класса arrayListType
и объявляет и определяет unorderedArrayListType
как шаблон.
-
main1.cpp
: тестовая программа для тестирования класса unorderedArrayListType
.
-
Makefile
Я получаю сообщение об ошибке компиляции при доступе к защищенным переменным arrayListType
в unorderedArrayListType
например: "длина, не объявленная в этой области", "список, не объявленный в этой области", где длина и список являются защищенными переменными в классе arrayListType
.
Ниже приведены коды:
arrayListType.h
#ifndef H_arrayListType
#define H_arrayListType
#include <iostream>
using namespace std;
template <class elemType>
class arrayListType
{
public:
const arrayListType<elemType>&operator=(const arrayListType<elemType>&);
bool isEmpty() const;
bool isFull() const;
int listSize() const;
int maxListSize() const;
void print() const;
bool isItemAtEqual(int location, const elemType& item) const;
virtual void insertAt(int location, const elemType& insertItem) = 0;
virtual void insertEnd(const elemType& insertItem) = 0;
void removeAt(int location);
void retrieveAt(int location, elemType& retItem) const;
virtual void replaceAt(int location, const elemType& repItem) = 0;
void clearList();
virtual int seqSearch(const elemType& searchItem) const;
virtual void remove(const elemType& removeItem) = 0;
arrayListType(int size = 100);
arrayListType(const arrayListType<elemType>& otherList);
virtual ~arrayListType();
protected:
elemType *list;
int length;
int maxSize;
};
template <class elemType>
bool arrayListType<elemType>::isEmpty() const
{
return (length == 0);
}
// remaining non-virtual functions of arrayListType class
#endif
unorderedArrayListType.h
#ifndef H_unorderedArrayListType
#define H_unorderedArrayListType
//#include <iostream>
#include "arrayListType.h"
//using namespace std;
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
public:
void insertAt(int location, const elemType& insertItem);
void insertEnd(const elemType& insertItem);
void replaceAt(int location, const elemType& repItem);
int seqSearch(const elemType& searchItem) const;
void remove(const elemType& removeItem);
unorderedArrayListType(int size = 100);
};
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = length; i > location; i--)
list[i] = list[i - 1];
list[location] = insertItem;
length++;
}
// Remaining virtual functions that need to be defined by the inherited class
#endif
main1.cpp
#include <iostream>
#include "unorderedArrayListType.h"
using namespace std;
int main()
{
unorderedArrayListType<int> intList(25);
int number;
cout<<"Line 3: Enter 8 integers: ";
for(int count = 0; count < 8; count++)
{
cin>>number;
intList.insertEnd(number);
}
cout<<"Line 8: intList: ";
intList.print();
cout<<endl;
}
Makefile:
all: main1
main1.o: main1.cpp
g++ -c -Wall main1.cpp
main1: main1.o
g++ -Wall main1.o -o main
clean:
rm -f *.o *~ main1
Ниже приведена ошибка компиляции:
make
g++ -c -Wall main1.cpp
In file included from main1.cpp:2:
unorderedArrayListType.h: In member function 'void unorderedArrayListType<elemType>::insertAt(int, const elemType&)':
unorderedArrayListType.h:30: error: 'length' was not declared in this scope
unorderedArrayListType.h:31: error: 'list' was not declared in this scope
unorderedArrayListType.h:33: error: 'list' was not declared in this scope
Дополнительные функции unorderedArrayListType
перечисленных и защищенных переменных указаны как не объявленные в области. Интересно, что может быть ошибкой.
Новая ошибка:
make
g++ -Wall main1.o -o main
Undefined first referenced
symbol in file
arrayListType<int>::seqSearch(int const&) constmain1.o
ld: fatal: Symbol referencing errors. No output written to main
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `main1'
Ответы
Ответ 1
Это связано с тем, что родительский элемент шаблона класса не создается во время этапа компиляции, который сначала проверяет шаблон. Эти имена, по-видимому, не зависят от конкретного экземпляра шаблона, и поэтому определения должны быть доступны. (Если вы никогда не посмотрите на определение arrayListType
, то, читая код unorderedArrayListType
вы arrayListType
list
а его length
должна быть своего рода глобальными.)
Вам нужно будет явно указать компилятору, что имена на самом деле зависят от создания родительского объекта.
Один способ, используя this->
перед всеми унаследованными именами: this->list
, this->length
.
Другой способ, используя объявления: using arrayListType<elemType>::length;
и т.д. (например, в закрытом разделе производного класса).
Запись с часто задаваемыми вопросами: https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members
Ответ 2
Расширенный комментарий к ответу от UncleBens.
Всегда хорошо иметь в виду, что шаблоны классов не являются классами. Это шаблоны. Один из способов взглянуть на это: в С++ классы не являются объектами. Вам нужно создать экземпляр класса для создания объекта. Аналогичная концепция применяется к шаблонам классов и классам. Подобно тому, как экземпляр класса создает объект, создание экземпляра класса создает класс.
Пока шаблон не будет создан, это отношение наследования, установленное между unorderedArrayListType
и arrayListType
, не существует. Компилятор не знает, собираетесь ли вы определить частичное создание шаблона arrayListType
, в котором нет элементов length
и list
. Вам нужно предоставить компилятору руку в unorderedArrayListType
с помощью this->length
и this->list
или какой-либо другой конструкции, которая сообщает компилятору, что вы ожидаете, что они будут членами данных.
Предположим, что вы используете this->length
в unorderedArrayListType
, и предположите, что кто-то приходит и пишет экземпляр частичного шаблона arrayListType<FooType>
, который не имеет length
и list
в качестве элементов данных. Теперь создание экземпляра unorderedArrayListType<FooType>
приведет к ошибке времени компиляции. Но так как вы не собираетесь это делать (вы не собираетесь это делать, не так ли?), Использование this->length
будет в порядке.
Ответ 3
Я бы попробовал две вещи:
1. Используйте this->
(что обычно полезно делать с шаблонами).
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = this->length; i > location; i--)
this->list[i] = this->list[i - 1];
this->list[location] = insertItem;
this->length++;
}
2. Typedef родительский элемент и используйте его при доступе к родительским элементам:
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
typedef arrayListType<elemType> Parent;
...
}
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = Parent::length; i > location; i--)
Parent::list[i] = Parent::list[i - 1];
Parent::list[location] = insertItem;
Parent::length++;
}