Присвоение производного объекта указателю базового класса внутри функции - PullRequest
0 голосов
/ 02 июля 2018

У меня есть базовый класс,

  struct MsgFormat {
    MsgFormat();
    virtual ~MsgFormat();

    const std::string rootNode;
  };

и производный класс,

 class ServiceMsg : public MsgFormat {
  public:    
    explicit ServiceMsg(std::string A, std::string B);
    ServiceMsg();
    ServiceMsg(const ServiceMsg& srvMsg);
    class ServiceMsg& operator=(const ServiceMsg& srvMsg);
    ~ServiceMsg();

    std::string A() const;
    std::string B() const;

  private:
    std::string m_A;
    std::string m_B;
};

Теперь у меня есть класс обработчика сообщений, который принимает указатель на базовый класс, например,

  class MsgHandler
  {
  public:
    MsgHandler();
    virtual ~MsgHandler();

    virtual int parseIncoming(struct MsgFormat* pBaseMsg) = 0;
    virtual int formatOutgoing(const struct HermesMsgFormat* const pBaseMsg) = 0;
  };

};

Теперь, если я сделаю что-то подобное в main (),

ServiceMsg* pSrvMsg = new ServiceMsg("foo", "bar");    
SpecificMsgHandler m_handle->formatOutgoing(pSrvMsg);

и внутри перегруженного метода formatOutgoing , у меня есть что-то вроде этого,

virtual int CustomMsgHandler::formatOutgoing(const struct MsgFormat* const pBaseMsg)
{
    const ServiceMsg* const pServiceMsg = dynamic_cast<const ServiceMsg* const>(pBaseMsg);
    std::cout << pServiceMsg->A() << std::endl;
    std::cout << pServiceMsg->B() << std::endl;
}

где CustomMsgHandler был унаследован от класса MsgHandler , все хорошо и хорошо. И это, я думаю, вся идея полиморфизма. Вы можете передавать объекты производного класса в интерфейс, который принимает указатель на базовый класс.


Что не работает, так это наоборот. Где parseIncoming () делает следующее,

    virtual int CustomMsgHandler::parseIncoming(struct MsgFormat* pBaseMsg)
    {
       pBaseMsg = new ServiceMsg("bla", "baa");

#ifdef NDEBUG
       ServiceMsg* pServiceMsg = dynamic_cast<ServiceMsg*>(pBaseMsg);
        std::cout << pServiceMsg->A() << std::endl;
        std::cout << pServiceMsg->B() << std::endl;
        std::cout << "all good here" << std::endl;
#endif
    }

Я делаю следующее в main ().

MsgFormat* pBaseMsg = new pBaseMsg();
SpecificMsgHandler m_handle->parseIncoming(pBaseMsg);

Но если я попытаюсь прочитать из pBaseMsg в main (), после возврата из вызова к parseIncoming , я не получу "bla" и "baa". Я получаю то, что было установлено в конструкторе по умолчанию.

ServiceMsg* pServiceMsg = dynamic_cast<ServiceMsg*>(pBaseMsg);
std::cout << pServiceMsg->A() << std::endl;
std::cout << pServiceMsg->B() << std::endl;
std::cout << "all good here" << std::endl;

Я не понимаю, почему этот 2-й случай не работает. Или как лучше заставить его работать?

1 Ответ

0 голосов
/ 02 июля 2018

Вы передаете указатель по значению. Функция создаст копию указателя, который вы передаете, затем вы измените копию. Поэтому изменения не видны в main после вызова функции.

Чтобы исправить это, вы можете вместо этого передать указатель по ссылке, изменив сигнатуру функции.

virtual int CustomMsgHandler::parseIncoming(struct MsgFormat*& pBaseMsg) //added &, it's now a reference to a pointer.

Также обратите внимание, что вы сначала делаете динамическое распределение в main.

MsgFormat* pBaseMsg = new pBaseMsg();

Затем вы передаете этот указатель в функцию и снова делаете динамическое распределение.

pBaseMsg = new ServiceMsg("bla", "baa");

Здесь вы сначала забыли delete указатель, так что у вас произошла утечка памяти. Каждый звонок на new должен иметь звонок на delete, чтобы освободить память. (Если он не передан как-то наподобие конструктора смарт-указателя, то смарт-указатель позаботится о delete для вас.)

При использовании c ++ 11 или буст-эквивалентов
На самом деле нет необходимости использовать голые new и delete в современном C ++. Вместо этого вы должны использовать такие вещи, как std::vector, std::unique_ptr и std::shared_ptr.

...