如何从基类调用派生类函数?

Posted

技术标签:

【中文标题】如何从基类调用派生类函数?【英文标题】:How to call derived class function from base class? 【发布时间】:2020-01-12 13:38:02 【问题描述】:

我是 C++ 的新手,我正在编写下面的代码。

这是DOCUMENT.h:它运行良好,因此我没有发布它的实现(document.cpp):

#pragma once
#include "stdafx.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
class Document 
private: 
    int id;
    char *titre;

public:
    Document();
    Document(int id, char *titre);
    ~Document();
    Document(const Document &doc);
    Document operator+( Document doc);
    Document operator=(const Document &doc);
    void setId(int id);
    int getId();

    void setTitre(char *titre);
    char *getTitre();
;

class Livre :public Document 
private:
    float price;

public:
    Livre();
    Livre(int id , char *titre,float price);
    ~Livre();
    Livre( Livre &doc);
    Livre operator+(Livre doc);
    Livre operator=( Livre &doc);

    void setPrice(float price);
    virtual float getPrice();
;

class Article :public Document 
private:
    char *date;

public:
    Article();
    Article(int id, char *titre , char *date);
    ~Article();
    Article(Article &doc);
    Article  operator+( Article &doc);
    Article operator=( Article &doc);
    void setDate(char* date);
    char *getDate();
;

MANAGER.H

#include "Document.h"

class Manager 
private:
    Document **doc ;

public:
    int count = 0;

    Manager();
    ~Manager();

    int  ajouter(Document *d);
    void afficher();

;

MANAGER.CPP

#include "stdafx.h"
#include "Manager.h"

Manager::Manager() 
Manager::~Manager() 

int  Manager::ajouter(Document *d) 
    if (count == 0) 
        doc = (Document**)malloc( sizeof(Document));
        doc[count] = new Document();

        doc[0] = d;
    
    else 
        doc = (Document**)realloc(doc, count * sizeof(Document));
        doc[count] = new Document();
        doc[count] = d;
    
    count++;

    return 0;


void Manager::afficher() 
    for (int i = 0; i < count; i++) 
        printf("\nid : %d  \n", doc[i]->getId());
        printf("titre : %s  \n", doc[i]->getTitre());
        printf("Price : %f  \n", doc[i]->getPrice());
    



我的问题出在afficher() 函数中。我想从那里打电话给getprice(),但是getprice() 不是Document 类的成员:它是Livre 类的成员(它是Document 的孩子)。有什么方法可以拨打getprice() 吗?

【问题讨论】:

mallocrealloc 使用不正确。如果你想以你描述的方式使用它,文档应该有一个virtual float getPrice();(可能应该是const)。 由于您使用了mallocrealloc,您的程序具有未定义的行为。两者的有效用途在 C++ 中非常罕见,我建议您忘记它们的存在。作为初学者,您正确使用它们的可能性很小。始终使用new[],或者更确切地说,根本不使用动态分配,而是使用std::vectorstd::string(以及std::unique_ptr 用于单个对象分配)。您几乎不必使用new/delete this 会回答您的问题吗?如果您需要更多解释,我可以将其变成完整的答案。 如果要调用Livre的成员函数,需要创建Livre类型的对象,而不是其基类Document。您希望Manager 持有指向DocumentLivre 实例的指针还是只持有后者?如果是前者,如果doc[i] 指向Livre 类型的对象,你想发生什么? 【参考方案1】:

您的问题的解决方案

理想情况下,Manager::afficher() 不必知道Document 的详细信息。如果应该将此委托给一个新功能Document::afficher(),并让每种类型的文档决定要打印哪些信息以及哪些信息可用。这就是多态的原理:

void Manager::afficher() 
    for (int i = 0; i < count; i++) 
        doc[i]->afficher();
    

如果你仍然想侵犯law of demeter,你可以保持你的方法。但是您只能使用可用于所有文档的元素。除非你确定它是 Livre。在这种情况下,您可以使用动态转换:

void Manager::afficher() 
    for (int i = 0; i < count; i++) 
        printf("\nid : %d  \n", doc[i]->getId());
        printf("titre : %s  \n", doc[i]->getTitre());
        Livre *l = dynamic_cast<Livre*>(doc[i]);  // attempt to see it's a Livre
        if (l)                      // If it's a Livre l is not null 
            printf("Price : %f  \n", doc[i]->getPrice());
        
        else printf ("Price : unknown\n"); 
    

不相关,但你的代码有很多你还不知道的问题

首先,C++ 不是 C,因此不应使用 malloc(),除非您确定自己在做什么(即,如果您知道何时使用 placement-new 的规则)。

此外,您对Manager 中的指针的管理将直接导致您陷入灾难:您分配了一个Document,您最终可以将其作为单个文档的数组来处理,但是使用强制转换魔法,您可以它是一个指向指针的指针。哎哟!

您需要改变管理文档集合的方式:

   doc = (Document**)malloc( sizeof(Document));   //!!!!! NOOO

由于您想使用具有不同类型专用文档的多态性,您可以使您的类看起来像:

class Manager 
private:
    vector<shared_ptr<Document>> doc ;  // safer way

public:
    Manager();
    ~Manager();
    int  ajouter(shared_ptr<Document> d);
    void afficher();
;

shared_ptr&lt;&gt; 像指针一样使用,但自己管理内存。 vector 是自动适应的:您只需要 push_back() 一个元素,它会在需要时自行调整大小。之后,您可以像使用数组一样索引元素。此外,vector::size() 对您很重要;

int  Manager::ajouter(shared_ptr<Document> d) 
    doc.push_back(d); 

int Manager::compter()        // you need to add this in your class definition
    return doc.size();         // always up-to date count ! 

如何将新文档添加到您的图书馆?

auto d1 = make_shared<Document>(); 
... 
manager.ajouter(d1); 
auto d2 = male_shared<Livre>(); 
...
manager.ajouter(d2); 

为了使其顺利运行,您还应该将 char* 替换为 string 以避免内存管理问题并获得 C++ 字符串的强大功能。

我在解决方案中提到的动态转换与共享指针的工作方式略有不同:您必须使用dynamic_pointer_cast 而不是dynamic_cast

最后一句话。每当你在一个类中有一个虚函数时,你也应该将析构函数设为虚函数。

【讨论】:

以上是关于如何从基类调用派生类函数?的主要内容,如果未能解决你的问题,请参考以下文章

从基类调用派生类函数

从基类调用派生类中的函数

如何从基类调用派生类方法?

如何通过派生类函数从基类更改向量

从基类c ++的向量中的派生类调用虚拟方法[重复]

使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?