将派生对象分配给函数内的基类指针

Posted

技术标签:

【中文标题】将派生对象分配给函数内的基类指针【英文标题】:Assigning a a derived object to a baseclass pointer inside a function 【发布时间】:2018-07-02 15:31:50 【问题描述】:

我有一个基类,

  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);

但是如果我尝试从 main() 中的 pBaseMsg 读取,在从调用返回 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;

我不明白为什么第二种情况不起作用。或者如何最好地让它发挥作用?

【问题讨论】:

分配给一个(非引用)参数在函数之外没有任何影响。 parseIncoming() 方法的目的是创建消息吗?从他们的名字来看,您的方法似乎应该做相反的事情。请说明您应该使用哪种方法(如果有)来创建消息。 @molbdnilo,我正在传递一个指针。我正在为函数内的该指针分配一个新对象。这与通过引用传递变量“实际上”有何不同? @r3musn0x,函数名称无关紧要,仅反映我正在尝试编写的模块。 parseIncoming() 和 formatOutgoing() 之间唯一真正的区别是,它接受已经在方法之外创建的派生类的对象。另一方面,第二个函数在方法内创建派生类的对象,并尝试将其分配给基类指针 - 即作为函数参数的基类指针。我期待一旦这个函数返回,基类指针就会在方法中分配给它的对象。 指针没有什么特别之处。如果要修改变量,则需要将指针(或引用)传递给该变量,无论该变量是否为指针。 【参考方案1】:

您正在按值传递指针。该函数将复制您传入的指针,然后您修改该副本。这就是函数调用后在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 以释放内存。 (除非它被传递给类似智能指针的构造函数,否则智能指针会为你处理deleteing。)

使用 c++11 或 boost 等效项时 在现代 C++ 中确实没有必要使用裸 newdelete。您应该改用 std::vectorstd::unique_ptrstd::shared_ptr 之类的东西。

【讨论】:

感谢您的回答。我将尝试您提到的“通过引用传递指针”解决方案,并将回复您。我有一种感觉,我必须这样做或使用指针。 关于内存泄漏,我稍后会在代码中删除指针。这只是真实事物的切碎和缩减版本,仅显示我有问题/怀疑的部分。但无论如何,谢谢你提到它。关于智能指针和现代 C++,谁说我使用的是现代 C++? :P 我绑定到项目中使用的编译器。这不是我的选择。手头没有 std::unique_ptr 和 std::shared_ptr 。如果你会说,那就使用 Boost,那么答案是,我不做那个决定。工具和库是固定的。我既没有 C++11/newer 也没有 Boost。 @JoeyMallone 只是为了在上面的评论中澄清您的问题,传递指针和对变量的引用实际上有何不同。当您传递一个指针时,指针指向的对象可以被修改,并且该更改将反映在函数之外。在您的示例中,您并没有这样做。您正在修改指针本身,而不是指针指向的对象。 @JoeyMallone 我明白了。在这种情况下,这确实是这样做的方法,除非您想编写自己的智能指针包装器。在未来读者的答案中指出这一点仍然很好。我将通过在该选项所需的 c++11 上方添加注释来澄清。 Alles klar!委员长! ;) 谢谢!

以上是关于将派生对象分配给函数内的基类指针的主要内容,如果未能解决你的问题,请参考以下文章

试图将派生类对象地址分配给基类指针向量

仅分配给对象的基类部分

为啥我可以通过指向派生对象的基类指针访问派生私有成员函数?

指向派生对象的基类指针的 C++ 排序容器

无法将指向派生类函数的指针传递给基类

在以抽象基类为参数的函数中将指针和赋值运算符与派生类一起使用