#c
#c
Вопрос:
У меня есть этот код, но я не вижу, где я ошибся здесь. Кажется, что он компилируется нормально, но я не могу получить доступ к Computer
или Appliance
функциям. Может кто-нибудь, пожалуйста, помочь мне понять, как я могу создать массив, содержащий разные объекты, в этом примере кода, который у меня здесь есть?
#include <iostream>
using namespace std;
class Technics
{
private:
int price, warranty;
static int objCount;
double pvn;
char *name, *manufacturer;
public:
Technics()
{
this->objCount ;
};
Technics(int price)
{
this->objCount ;
this->price = price;
}
~Technics(){
this->objCount = this->objCount - 2;
};
static int getObjCount()
{
return objCount;
}
void setPrice(int price)
{
this->price = price;
}
int getPrice()
{
return this->price;
}
void resetCount()
{
this->objCount = 0;
}
};
int Technics::objCount = 0;
class Computer : public Technics
{
private:
int cpu, ram, psu, hdd;
public:
Computer() {}
Computer(int price)
{
this->setPrice(price);
}
void setCpu(int cpu)
{
this->cpu = cpu;
}
int getCpu()
{
return this->cpu;
}
};
class Appliance : public Technics
{
private:
int height;
int width;
char* color;
char* type;
public:
Appliance(){}
Appliance(int height, int width)
{
this->height = height;
this->width = width;
}
void setWidth(int width)
{
this->width = width;
}
int getWidth()
{
return this->width;
}
};
void main()
{
//Creating array
Technics *_t[100];
// Adding some objects
_t[0] = new Computer();
_t[1] = new Computer();
_t[2] = new Appliance();
// I can access only properties of Technics, not Computer or Appliance
_t[0]->
int x;
cin >> x;
}
Комментарии:
1. Вероятно, лучше всего взять хорошую книгу о C и разобраться в концепциях наследования и полиморфизма. Кстати, не существует такого понятия, как «массив различных объектов»: все элементы вашего массива имеют один и тот же тип, а именно
Technics *
.2. Ну, есть массив boost::variant<…>, из boost::any или из void *, конечно… (по возрастающей степени безумия)
3. Конечно, вы можете получить доступ только к свойствам Technics, потому что это указатель на
Technics
. ТакжеTechnics *_t[100]
является указателем на массивTechnics
. Это не то, что вы хотите. Пожалуйста, используйтеstd::vector
4. Спасибо, я заставил его работать со static_cast(); Если у кого-нибудь есть предложения по поводу хорошей книги по этому вопросу, пожалуйста, оставьте в комментариях.
Ответ №1:
Строка:
_t[0] = new Computer();
Создает компьютерный объект и сохраняет его как базовый указатель Technics в массиве (т.е. по сути, находясь в этом массиве, он является объектом Technics).
Вам нужно выполнить обратное приведение к производному классу, чтобы получить доступ к членам, которые являются более производными, чем элементы Technics:
static_cast<Computer*>(_t[0])->Your_Member();
Используйте dyncamic cast, если вы не знаете, какой это производный тип — он вернет приведенный указатель при успешном выполнении и NULL при сбое, так что это своего рода проверка типа — хотя это требует больших затрат времени выполнения, поэтому постарайтесь избежать этого 🙂
ОТРЕДАКТИРУЙТЕ в ответ на ваш заключительный комментарий:
//Calculate the length of your dynamic array.
//Allocate the dynamic array as a pointer to a pointer to Technics - this is like
//Making an array of pointers each holding some Technics heirarchy object.
Technics** baselist = new Technics*[some_length];
//Populate them the same way as before:
baselist[0] = new Computer();
baselist[1] = new Appliance();
PS: вы также можете использовать std:: vector, который динамически изменяется, в отличие от просто созданного во время выполнения — это лучший вариант, если вам разрешено его использовать. Это избавляет вас от необходимости создавать собственный код массива с изменяемым размером. Погуглите это 😉
Комментарии:
1. Если вы не уверены, что это
_t[0]
являетсяComputer
, вы можете использоватьdynamic_cast
вместо этого, но будьте осторожны, что оно может вернутьсяNULL
, если приведение завершится неудачей.2. Ах, хорошо, это должно научить меня не находиться в операционной системе в одном окне и выполнять работу в другом. 🙂
3. Ха-ха, да. Честно говоря, я делаю это постоянно: p Когда ты программист, иногда трудно не просто прочитать код и проигнорировать остальное.
4. Спасибо, чувак, это сработало. Я уже искал различия между статическими и динамическими приведениями. Во всяком случае, я пытался сделать этот массив динамическим обычными методами, но безуспешно. Если у вас есть немного времени, чтобы помочь мне, пожалуйста, сделайте это и покажите мне, как я могу создать динамический массив этих объектов сейчас вместо статического. 🙂
Ответ №2:
Это потому, что _t является указателем на Technics
не Computer
или Appliance
.
Укажите Technics параметр «тип объекта», например, перечисление, которое является TechnicsType.Computer
for Computer
и TechnicsType.Applicance
for Appliance
, проверьте это и приведите к соответствующему типу, чтобы получить методы класса.
Ответ №3:
Решение очень, очень простое 🙂
Суперкласс должен иметь виртуальные функции подклассов, объявленных в определении класса.
Например: если у компьютера суперкласса есть подкласс с именем laptop, который имеет функцию int getBatteryLife();
, то класс computer должен иметь определение virtual int getBatteryLife()
, которое будет вызываться в векторе указателей типа computer.
Комментарии:
1. Проблема этого решения заключается в том, что вы должны объявить множество виртуальных функций в базовом классе, по одной на атрибут подкласса, который вы хотите использовать.
Ответ №4:
Поскольку _t
это Technics
массив указателей и, доступ к атрибутам производных классов невозможен. Используйте шаблон посетителя, подобный этому, или опустите указатель:
// visitor pattern
class Visitor
{
void accept(Appliance amp;ref) { // access Appliance attributes };
void accept(Computer amp; ref) { // access Computer attributes };
};
class Technics
{
....
virtual void visit(Visitor amp;) = 0;
};
class Appliance
{
....
virtual void visit(Visitor amp;v) { v.accept(*this); }
};
class Computer
{
....
virtual void visit(Visitor amp;v) { v.accept(*this); }
};
Ответ №5:
Да, вы можете получить доступ только к свойствам Technics, поскольку ваша переменная имеет тип Technics
. Вы должны привести его к классу вашего компьютера или устройства, чтобы выполнить ваши дополнительные методы.
Здесь вам действительно нужно подумать о своем дизайне. Действительно ли это подходит? Почему у вас все объекты находятся внутри одного контейнера? Особенно, если у вас есть разные методы для вызова .. это не имеет смысла..
Если вы действительно хотите вызывать разные методы, вам, вероятно, придется использовать switch
инструкцию, чтобы решить, какой у вас тип, а затем вызвать подходящие методы (я предполагаю, что вы хотите выполнить итерацию по всему контейнеру, иначе нет смысла иметь большой контейнер с разными объектами).