使用具有抽象基类指针并调用派生类函数的映射

Posted

技术标签:

【中文标题】使用具有抽象基类指针并调用派生类函数的映射【英文标题】:Using a map that has a abstract base class pointer and calling a derived class function 【发布时间】:2016-05-15 21:32:07 【问题描述】:

我在网上搜索过,找不到任何解决我的问题的方法,希望您能提供帮助。

所以我构造了一个抽象基类,并有两个派生类代表不同的实验。 (一个实际上是我的基类的派生类)我在一个单独的头文件中制作了一个这样的映射来存储不同类型的实验。

//Map class template to store name of experiment and the experiment within a project
typedef map <string, baseData <double>*> ExpContainer;

void search(string searchName, ExpContainer exps) 
    ExpContainer::iterator Iter;
    Iter = exps.find(searchName); //finds the entry corresponding to searchName and returns the iterator
    if (Iter != exps.end())  //need this as if entry is not found, the return will be end iter.
        cout << "Found the experiment " << Iter->first << "." << endl;
        Iter->second->print();
    
    else 

        cout << "Sorry, experiment " << searchName << " is not found" << endl;
    


每种实验类型的 print() 函数都不同,我知道存在一个称为切片的问题,因此我确保 print() 在基类中是虚拟的。这是我的基类:

namespace mynmsp 

//base class of data can be measurements or errors
template < class T> class baseData  

public:
    virtual void print() =0;
    virtual ~baseData() 
        cout << "Destructor called for the base class." << endl;
    
;

然后在我的 main.cpp 中,我构建了不同类型的实验,我想打印它们。每个实验类都有不同的 print 函数实现,它覆盖了基类中的 print 函数,例如:

void print() //do something ; 

在我的 main.cpp 中,我将地图定义为:

ExpContainer project;

在我构建了每个实验之后,我向用户询问了实验的名称 (expName) 并插入到项目中:

project[expName] =  &expC;

我认为插入很好,因为我测试了项目的大小并且它是正确的。 但是,当我的搜索函数被这样调用时发生了运行时错误:

search(name, project);

不知道是切片有问题还是我的指针有问题? 我试图在每个派生类中使 print() 成为一个虚函数,但这似乎也不起作用。

抱歉,问题很长,请帮忙!

编辑:我在 do while 循环中构建了我的实验,而项目在外部声明。整个代码很长,但它的基础是这样的:

string repeatstr; //user input whether to repeat do while loop or not
bool repeat = true; //condition for staying inside do while loop
ExpContainer project; //declared project to store experiments

do 
    string expName;
    string ans1; //character to store user input
    cout << "What is the name of your experiment? " << endl;
    cin >> expName;
    cout << "Is this a experiment C ? (y/n)" << endl;
    cin >> ans1; 
       if(ans1 =="y") 
           //call the constructor for expC
           project[expName] = &expC;
        else 
           //call the constructor for expB 
          project[expName] = &expB;
        

    cout << "Do you want to enter another experiment?  (y/n)" << endl;
    cin >> repeatstr;
    if (repeatstr == "n")  repeat = false;  

while (repeat); //loop over this do-while loop while repeat is true

    cout << "There are " << project.size() << " in this database." << endl;

    //to retrieve info from a certain experiment
    string input, name;
    cout << "Would you like to retrieve any experiments (y/n)? " << endl;
    input = EitherInput("y", "n");
    if (input == "y") 
        cout << "Please enter the name of the experiment you want to retrieve: " << endl;
        cin >> name;
        search(name, project); //code breaks down here!
    

【问题讨论】:

您是否使用过调试器? 您确定expC 对象实际上比地图寿命更长吗? @KerrekSB ,您好,感谢您的回复。我在 do while 循环内构建了 expC,并在循环外声明了 project。所以不,但我将 expC 推到 do while 循环内的项目中......所以应该没问题吗?项目的大小也是正确的。 嗨@ArchbishopOfBanterbury,感谢您的快速回复。恐怕我处于中级编程水平,不知道如何使用调试器..... 尽早使用调试器很有用,因为通过在执行阶段识别每个变量的值更容易解决这种情况。也就是说,如果您在此处的问题中显示 do-while 循环的相关部分可能会很有用,因为这可能是问题的根源。 【参考方案1】:

您正在保存指向已销毁对象的指针。您可以检查地图中的地址,很可能它们是相同的。您应该将实验对象存储在动态内存中

if(ans1 =="y")
 
    project[expName] = new expC();
 // Scope ends here and all variable created in it will be destroyed.
else 

  project[expName] = new expB();
 // Scope ends here and all variable created in it will be destroyed.

在你完成它们之后,你需要在每个指针上调用delete 以避免内存泄漏。您还需要检查地图中的项目是否已经存在,否则您将丢失指向已分配内存的指针,这会自动导致内存泄漏。

我建议您使用std::share_ptr&lt; baseData&lt;double&gt; &gt; 而不是裸露的baseData&lt;double&gt;*。 Here 你可以阅读更多关于它的信息。并且还可以考虑使用 typedef 以获得更清晰的语法。


附: 功能

void search(string searchName, ExpContainer exps)

会将整个地图复制到其主体。改用常量引用

void search(string searchName, const ExpContainer& exps)

但是你还需要将函数 print 声明为 const:

virtual void print() const = 0;

并用 const 修饰符覆盖它:

virtual void print() const override;

并使用常量迭代器ExpContainer::const_iterator Iter

【讨论】:

谢谢@teivaz,但是我有一个小问题,因为我的代码比我构建 expC 和 expB 后的代码要复杂一些,我必须对其进行操作,然后将它们存储在项目中。如果没有“新”,我就无法将操纵的实验存储到项目中吗?就像你如何在向量中执行 .pushback 一样? expC* myExperimentC = new expC(); project[expName] = myExperimentC; do stuff to myExperimentC; @AliS 你甚至可以使用引用来使用点运算符而不是箭头:expC&amp; myExperimentCRef = * myExperimentC; 当我使用const下面的行Iter = exps.find(searchName); 它有一个错误:没有运算符'='匹配这些操作数 简单但错误的方法是从参数中删除 const。正确的方法是将虚函数 print 声明为 const 并使用 const 迭代器。我会更新我的答案

以上是关于使用具有抽象基类指针并调用派生类函数的映射的主要内容,如果未能解决你的问题,请参考以下文章

关于C++基类、派生类的引用和指针

派生自抽象基类并调用另一个类中的方法的 C++ 包装类,该类也派生自抽象基类

虚函数和基类中的this指针的问题!

如何从基类调用派生赋值运算符?

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

为啥指向基类的派生类指针可以调用派生类成员函数? [复制]