Создание массива различных объектов

#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 инструкцию, чтобы решить, какой у вас тип, а затем вызвать подходящие методы (я предполагаю, что вы хотите выполнить итерацию по всему контейнеру, иначе нет смысла иметь большой контейнер с разными объектами).