Можете ли вы сделать указатель на класс в C ++? - PullRequest
0 голосов
/ 06 января 2020

Примечание: я не имею в виду указатель на ОБЪЕКТ или МОМЕНТ, я имею в виду указатель на КЛАСС

Я ставлю это сверху, потому что я знаю, если я не люди скажет мне, что я могу сделать

class MyClass;
MyClass* pointerToMyClass = new MyClass(); // Ta-da! pointer to class!

Это не то, что я ищу. Я хотел бы сделать указатель на сам тип класса, что-то вроде указателя на функцию, но для класса.

Чтобы пояснить, о чем я спрашиваю, приведу код, который примерно демонстрирует, что я ищу.

class MyClass; // real Class
class MyDerived : public MyClass; // derived class

class * ClassPointer = &MyClass; // pointer to my class
MyClass * instanceOfPointer = new ClassPointer(); // now makes an instance of MyClass

ClassPointer = &MyDerived; // pointer to derived class
MyClass * instance2 = new ClassPointer(); // now makes an instance of DerivedClass

Так что вроде как typedef, но более динамичный c.

Я также хотел бы иметь возможность создать вектор указателей классов. В настоящее время я взламываю нужное мне поведение с помощью функции класса clone(), которая возвращает новый указатель на базовый класс, но мне не особенно нравится этот подход.

Возможно ли что-либо подобное в C ++?

Редактировать: текущая проблема, для которой я использую это, - сетевой протокол polymorphi c. Существует базовый класс пакета:

class packet{
    int id=0;
    virtual packet* read(); // read packet type into id
    virtual packet* clone(){ return new packet(); }
};

, затем несколько производных пакетов:

class packet1 : public packet{
    int id=1
    packet* read(); // do packet specific reading
    packet * clone(){ return new packet1(); }
};

class packet2 : public packet{
    int id=2
    packet* read(); // do packet specific reading
    packet * clone(){ return new packet21(); }
};

class packet3 : public packet{
    int id=3
    packet* read(); // do packet specific reading
    packet * clone(){ return new packet3(); }
};

, а затем для чтения пакета все, что вам нужно сделать, это

packet* array[] = { new packet(), new packet1(), new packet2(), new packet3() }; // array of packets, this should be class* if possible
packet type = packet();
type.read(); // read type from network
packet* data = array[type.id].clone(); // pick derived class based on type
data->read(); // read specific packet from network

packet*[] будет заменен на class*[], чтобы избежать необходимости использовать clone().

Это упрощенный, но выполнимый пример.

Ответы [ 2 ]

2 голосов
/ 06 января 2020

Нет, это невозможно, потому что типы должны быть разрешены во время компиляции. Вы «физически» компилируете в инструкции, как создавать новые объекты. Конечно, вы можете сделать его переменным с помощью простого условия на основе typeinfo с использованием RTTI.

#include <typeinfo>
MyClass *createInstanceOf(const std::type_info& myType)
{
    if (myType == typeid(MyClass))
        return new MyClass();
    if (myType == typeid(MyDerived))
        return new MyDerived();
    // handle invalid type
}

Таким образом, вы можете создавать различные объекты на основе переменной времени выполнения (которая является переменной std :: type_info).

class MyClass;//real Class
class MyDerived: public MyClass;//derived class
std::type_info myType = typeid(MyClass);//pointer to my class
MyClass * instanceOfPointer = createInstanceOf(myType);//now makes an instance of MyClass
myType = typeid(MyDerived);//pointer to derived class
MyClass * instance2 = createInstanceOf(myType);//now makes an instance of DerivedClass

Важной частью является то, что эта функция знает, в каком объекте должно быть скомпилировано создание (все типы, которые вы хотите поддерживать), решение о том, какой путь выбран, принимается во время выполнения, но само создание известно, когда вы скомпилируйте код. Типы никогда не могут быть переменными. Даже шаблоны не являются переменными, они генерируют экземпляры функций / классов для каждого типа, для которого он был создан во время компиляции.

Добавлю, что это решение полезно только в очень специфических c случаях. По сути, если вы знаете тип во время компиляции, то вы никогда не должны сохранять type_info этого типа, а затем использовать его по условию, чтобы получить тип обратно. Это будет намного тяжелее, чем должно быть, и почти невозможно оптимизировать.

Кажется, в вашем примере чего-то не хватает. Как я понимаю, все, что вы хотите сделать, это прочитать тип из внешнего источника, а затем прочитать данные на основе этого типа переменной. Это довольно стандартный способ чтения любых внешних сообщений и выполняется более или менее так, как я упоминал выше. Вам нужен механизм принятия решений во время выполнения, который будет принимать правильный путь для данного типа. В какой-то момент вам придется принять это решение, потому что ваш код должен разветвляться и обращаться к правильному обработчику (который в вашем примере выглядит как пакет). В этом случае вы не должны использовать typeid, поскольку он гарантированно будет уникальным и одинаковым для одного и того же типа в одном исполнении программы - он не может быть разделен или сохранен для следующего выполнения. В вашем случае вам нужна информация о вашем типе (обычно enum наиболее эффективен). Таким образом, вы можете создать карту ваших обработчиков. Если типы пакетов известны и хорошо определены, то вы можете сделать их в порядке от 0 и сохранить их в регулярном массиве с постоянным временем доступа. Это один из самых эффективных способов сделать это. По сути это будет ваш type.id.

0 голосов
/ 18 февраля 2020

Лучший ответ никогда не выдавался за ответ, кредит Раймонду Чену

Нет, вы не можете взять указатель на класс (и на самом деле запрещено брать указатель конструктора http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf 12.1.12), однако вы можете сделать указатель на фабрику объектов с помощью

template<typename T> T* make<T>() { return new T(); }

и взять указатель на функцию make<classToPointTo>

Гораздо лучше, чем моя функция клонирования и Я не хотел, чтобы этот замечательный ответ оставался в комментариях до конца.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...