【问题标题】:Why is this explicit scope resolution necessary?为什么这个明确的范围解析是必要的?
【发布时间】:2010-12-25 13:19:21
【问题描述】:

设置:

class A {
  public:
    void a() {}
};

class B {
  public:
    void b() {}
};

class C: public A, public B {
  public:
    void c() {}
};

(我认为)我应该能够做什么:

C* foo = new C();
foo->b();

我从 GCC 收到以下链接器错误:

`... undefined reference to 'C::b(void)'`

如果我使用显式范围解析,它会起作用,如下所示:

C* foo = new C();
foo->B::b();

A、B 和 C 不共享具有相似签名的成员,所以我知道没有隐藏任何内容。两个问题:

1) 为什么理论上我不能隐式访问公共基类成员?

2) 在实践中我可以做些什么(如果有的话)来避免这种烦人的语法?

我可以继续使用显式语法进行开发(尽管我宁愿摆脱它​​),但我更希望在这里了解我(显然不正确)关于 C++ 中公共继承的知识。

干杯!

编辑:抱歉,更新了示例代码 - 我的原始代码中有一些严重的错误输入。

EDIT2:这是真实代码的(相关部分): 来自 src/creature.h:

#include "container.h"
#include "identifiers.h"

class Creature: public Identifiers, public Container {
  public:
    Creature( void );
    Creature( const Creature& ref );
    virtual ~Creature( void );
};

来自 src/identifier.h:

class Identifiers {
  public:
    Identifiers( void );
    Identifiers( const Identifiers& ref );
    ~Identifiers( void );
};

来自 src/container.h:

class Container {
  public:
    Container( std::string (Object::*getName)( void ) const );
    Container( const Container& ref );
    ~Container( void );

    void  add( Object* object );
    void  add( const std::list<Object*>& objects );
    void  remove( Object* object );
    void  remove( const std::list<Object*>& objects );
};

来自 src/container.cpp:

Container::Container( std::string (Object::*getName)( void ) const ) {
  _getName = getName;
  return;
}

Container::Container( const Container& ref ) {
  _getName = ref._getName;
  return;
}

Container::~Container( void ) {
  while ( !objectList().empty() ) {
    delete objectList().front();
    objectList().pop_front();
  }
  return;
}

void Container::add( Object* object ) {
  objectList().push_back( object );
  return;
}

void Container::add( const std::list<Object*>& objects ) {
  objectList().insert( objectList().end(), objects.begin(), objects.end() );
  return;
}

void Container::remove( Object* object ) {
  objectList().remove( object );
  return;
}

void Container::remove( const std::list<Object*>& objects ) {
  for ( std::list<Object*>::const_iterator it = objects.begin(); it != objects.end(); ++it ) {
    remove( *it );
  }
  return;
}

来自应用程序本身:

bool CmdWear::execute( Creature* creature, const std::string& args ) {
  std::list<Object*> objects;
  Object* removed = NULL;
  std::string error;

  objects = creature->searchObjects( args );

  for ( std::list<Object*>::iterator it = objects.begin(); it != objects.end(); ++it ) {
    if ( creature->wear( *it, error, removed ) ) {
      creature->remove( *it );
    }
  }

  return true;
}

注意事项:

1) 此处显示的所有方法均在其相应的 .cpp 文件中定义。

2) 每个关联的目标文件都编译得很好。只有链接器在将所有目标文件链接在一起的最后一步失败。

3) GCC 吐出:对 `Creature::remove(Object*)' 的未定义引用,除非我通过以下方式明确限定范围:

creature->Container::remove( *it );

生成文件

PROJECT     = symphony
CPPC        = g++
FLAGS_DEV   = -ggdb3 -ansi -Wall -Werror -pedantic-errors
LIBS        = `pcre-config --libs` `mysql_config --libs`
SRC_DIR     = src
OBJ_DIR     = obj
BIN_DIR     = .
SRC_FILES  := $(wildcard $(SRC_DIR)/*.cpp)
OBJ_FILES  := $(patsubst src/%.cpp,obj/%.o,$(SRC_FILES))

dev: $(OBJ_FILES)
  $(CPPC) $(LIBS) $(FLAGS_DEV) $(OBJ_FILES) -o $(BIN_DIR)/$(PROJECT)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
  $(CPPC) -c $(FLAGS_DEV) $< -o $@

【问题讨论】:

  • Alex 下面的回答是正确的;但是,我认为您在编写此测试用例时遗漏了一些东西,并且您的真实代码可能正在做一些稍微不同的事情。 (由于您忘记将 'foo' 设为指针,因此引用的错误消息显然来自其他代码。)
  • 我认为您遗漏了一些细节。 “C foo = 新 C();”单独应该不编译,因为 foo 需要是一个指针。
  • 好吧,既然您已经更改(修复)了您的代码,它不再给出您声称的链接器错误。所以你的问题不再有意义。
  • 请剪切并粘贴(不要重新输入)完整的代码来证明您的错误。
  • 不需要真正的代码,只要最小的测试用例正确地演示了同样的问题。

标签: c++ inheritance gcc scope visibility


【解决方案1】:

您不能调用未定义的函数。 gcc 就是这样告诉你的。而foo::B-&gt;b(); 不是有效的 C++。它不能用g++cl.exe 编译。

如果你写了

public:
  void b() {}

它会起作用的。

【讨论】:

  • 但是 C 从 A 和 B 公开继承,并且 B 确实定义了 b(void)。这在带有 -ansi 标志的 GCC 4.4.1 上编译。
  • B 没有定义 b(void)。它只声明它。
  • 抱歉 - 这些方法已定义。感谢您的提醒。
  • @Chris,声明和定义是有区别的。 B::b() 已声明,但未定义。此外,正确的语法应该是 foo-&gt;B::b(),这对你一点帮助都没有,因为 B::b() 仍然没有在任何地方定义。
  • 如果B 是具有指针类型的foo 的静态成员,这可能是有效的......我认为我们没有得到所有代码。
【解决方案2】:

我认为您发布的任何一个 sn-ps 都不应该编译。

class C: public A, public B {
  public
    void c();
};

您需要在public 访问说明符之后添加:

C foo = new C();
foo->b();

您正在尝试从指向动态分配的C 对象的指针初始化C. 不是 -&gt; 是在对象类型上调用函数的正确运算符。 -&gt; 仅适用于指针类型(或具有重载 -&gt; 运算符的对象)。

你需要这样的东西:

C foo;
foo.b();

我不确定你说的 sn-p 是如何工作的。

【讨论】:

    【解决方案3】:
    C foo = new C();
    foo->b();
    

    这是错误的,你可能想要:

    C foo;
    foo.b();
    

    只要您收到的错误不是链接器错误(我不熟悉 gcc 套件),它应该可以工作。如果是链接器错误,则意味着您的函数没有主体,因此没有可调用的内容。

    【讨论】:

    • 两者都可以。虽然堆栈分配是首选,但这与问题无关。
    • 不,C foo = new C();不起作用,因为 new C() 将返回一个指针。在他的例子中, foo 的定义是错误的。 C * foo = 新 C();会工作的
    【解决方案4】:

    您得到的错误是链接器错误,而不是编译器错误。这意味着您的代码可以编译——很好。链接器抱怨,因为它找不到函数的定义。定义是否在不同的 C++ 文件中?也许有一个文件 B.cpp 包含:

    #include "B.h"
    void B::b() {
      // do something nifty here
    }
    

    然后有一个单独的文件 C.cpp 包含:

    #include "C.h"
    C *
    func() {
      C* foo = new C();
      foo->b();
      return foo;
    }
    

    如果只编译C.cpp,如:

    g++ -Wall -Wextra C.cpp
    

    然后你会得到你所看到的链接器错误。

    也许您想进行部分编译,然后稍后链接所有 .o 文件?

    g++ -Wall -Wextra -c C.cpp
    

    或者您想将它们全部编译在一起并同时链接?

    g++ -Wall -Wextra A.cpp B.cpp C.cpp
    

    【讨论】:

      【解决方案5】:
      Try this out :
      
      C *foo = new C();
      
      And where is the function definitions ??.
      
      Explicit scope resolution is only necessary in this case:
      
      class A {
       public:
           void fun(){};
      };
      
      class B {
       public:
           void fun(){};
      };
      
      class C : public A , public B
      {
      
      };
      
      int main()
      {
      
         C c;
      
         c.A::fun();
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-20
        • 2012-03-09
        • 1970-01-01
        • 1970-01-01
        • 2011-12-14
        • 1970-01-01
        • 2016-11-22
        相关资源
        最近更新 更多