【问题标题】:c++ pointers to base class has not got derived class member functions but pointer is created with derivedc++ 指向基类的指针没有派生类成员函数,但指针是用派生的
【发布时间】:2017-07-18 11:47:12
【问题描述】:

我创建了一个类,它是指向基类的指针的容器。我可以添加基类对象以及派生类对象。但是,当我尝试使用派生类对象中的函数时,出现编译错误“'Base' class has not member named 'name'”。 代码如下:

    #include <cstdio>
    #include <vector>

    using namespace std;

    enum Type{
        obj,
        obj1,
        obj2
    };

    class Base{
        public:
            void fun(){
                printf("I'm base\n");
            }
    };

    class Derived1 : public Base{
        public:
            void fun2(){
                printf("I'm derived1\n");
            }
    };

    class Derived2 : public Base{
        public:
            void fun3(){
                printf("I'm derived2\n");
            }
    };

    class myVector{
        private:
            vector<Base*> objects;
        public:
            void add(Type type){
                switch(type){
                    case obj:
                        objects.push_back(new Base);
                        break;
                    case obj1:
                        objects.push_back(new Derived1);
                        break;
                    case obj2:
                        objects.push_back(new Derived2);
                        break;
                }
            }

            Base* operator[](unsigned index){
                return objects[index];
            }
    };

    int main(){
        myVector vec;
        vec.add(obj);
        vec.add(obj1);
        vec.add(obj2);
        vec[0]->fun();     // ok
        vec[1]->fun1();    // error "Base" has not member named "fun1"
        vec[2]->fun2();    // error "Base" has not member named "fun2"
    }

应该打印出来

我是基地
我是派生的1
我是派生2

但我在 cmets 中的代码中出现编译错误

【问题讨论】:

  • 函数名fun2fun3 不能通过Base* 访问。
  • 如果要通过基类指针调用函数,必须在基类中声明。
  • 您当然可以static_cast&lt;Derived1*&gt;(vec[1])-&gt;fun1() - 但这通常表明您在设计类层次结构时出错了。您需要以不同的方式处理子类,是否有一个很好的现实原因,而这不是用虚拟方法解决的?

标签: c++ pointers inheritance derived-class base-class


【解决方案1】:

C++ 在编译时静态解析名称。你告诉它你有一个Base*。这些是您可以通过该指针在该对象上使用的名称。想要访问Derived 中的那些你必须投射。

【讨论】:

    【解决方案2】:

    你应该把它变成一个虚函数并在派生类中重写它

    #include <cstdio>
    #include <vector>
    
    using namespace std;
    
    enum Type{
        obj,
        obj1,
        obj2
    };
    
    class Base{
        public:
            virtual void fun(){
                printf("I'm base\n");
            }
    };
    
    class Derived1 : public Base{
        public:
            void fun(){
                printf("I'm derived1\n");
            }
    };
    
    class Derived2 : public Base{
        public:
            void fun(){
                printf("I'm derived2\n");
            }
    };
    
    class myVector{
        private:
            vector<Base*> objects;
        public:
            void add(Type type){
                switch(type){
                    case obj:
                        objects.push_back(new Base);
                        break;
                    case obj1:
                        objects.push_back(new Derived1);
                        break;
                    case obj2:
                        objects.push_back(new Derived2);
                        break;
                }
            }
    
            Base* operator[](unsigned index){
                return objects[index];
            }
    };
    
    int main(){
        myVector vec;
        vec.add(obj);
        vec.add(obj1);
        vec.add(obj2);
        vec[0]->fun();     // ok
        vec[1]->fun();     // now it works
        vec[2]->fun();     // now it works
    }
    

    【讨论】:

      【解决方案3】:

      如果您希望能够通过基类指针调用方法,那么这些方法需要在基类中定义(可能是纯虚方法)。

      如果你知道你有一个子类对象,你可能想static_cast(或者可能dynamic_cast和测试),并通过转换指针调用方法。

      如果不了解 fun1()fun2() 的语义,就不可能知道哪种替代方案最适合您的情况。

      这是一个可以满足您要求的版本 - 请注意 Base::fun 被声明为 virtual 并且子类实现会覆盖它。

      #include <iostream>
      #include <vector>
      
      struct Base {
          virtual void fun() const {
              std::cout << "I'm base" << std::endl;
          }
          virtual ~Base() = default;
      };
      
      struct Derived1 : public Base{
          void fun() const override {
              std::cout <<  "I'm derived1" << std::endl;
          }
      };
      
      struct Derived2 : public Base{
          void fun() const override {
              std::cout << "I'm derived2" << std::endl;
          }
      };
      
      int main(){
          std::vector<Base*> vec{new Base(), new Derived1(), new Derived2()};
          vec[0]->fun();
          vec[1]->fun();
          vec[2]->fun();
      
          // cleanup
          for (auto e: vec)
              delete e;
      }
      

      【讨论】:

      • 如果我将 switch 语句放入 operator[] 并在那里转换它会起作用吗?或者我必须在调用后转换它?
      • 如果你将它投射到你的operator[],那将毫无意义,因为当你从那里返回它时,它又是一个Base*。要正确回答您的问题,需要更多背景知识,说明您为什么认为需要特别对待不同的派生类 - 这通常表明存在更严重的设计问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      • 2016-10-10
      • 1970-01-01
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多