头文件中定义的非内联虚函数

时间:2013-06-17 06:42:17

标签: c++ one-definition-rule

  

One Definition Rule规定:
  在整个程序中,一个对象   或非内联函数不能有多个定义。 (从   维基百科)

嗯,我知道如果在头文件中定义了一个成员函数,它就会隐式内联,并且可以使用ODR。

但虚拟功能怎么样?我们知道,如果虚函数被称为多态,则无法内联。如果在头文件中定义了这样的虚函数,是否会违反ODR?

例如:

//derived.hpp
#include <iostream>
class Base {
public:
  virtual ~Base() {}
  virtual void vfunc() {
    std::cout << "Base::vfunc()\n";
  }
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc() {
    std::cout << "Derived::vfunc()\n";
  }
};

//foo.cpp

#include "derived.hpp"
void func() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
}
//main.cpp

#include "derived.hpp"

int main() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
  return 0;
}

我很好奇:

vfunc(和dtor)在foo.cpp和main.cpp中都是多态的(不是内联的),这意味着它在整个程序中被定义了两次,所以它违反了ODR,不是吗?它如何编译(链接)?

我刚看到:

  

不止一个定义

     

在某些情况下,可以有多个类型或a的定义   模板。由多个头文件和源组成的程序   文件通常只有一个类型的定义,但不是   每个翻译单元不止一个定义。如果程序包含   一个类型的多个定义,那么每个定义必须是   等价物(也取自维基百科)

certain cases是什么?以上案例是否属于其中之一?

2 个答案:

答案 0 :(得分:2)

  

vfunc(和dtor)在foo.cpp和main.cpp中以多态方式调用(非内联),这意味着它在整个程序中定义了两次,

首先:调用并不意味着定义。因此,函数调用不会告诉您ODR是否被违反。

  

所以它违反了ODR,不是吗?它如何编译(链接)?

它编译,因为在类定义中定义的成员函数是隐式声明的内联,因此不违反ODR。这适用于vfunc定义以及dtors,所以你在这里很好。

注意:函数声明 inline(显式或隐式)与实际内联函数之间存在差异。编译器内联函数一次的决定可能受inline关键字的影响,但它只是一个提示。这些天优化器可以比任何人更好地预测何时何时不内联是一个不错的选择,因此编译器可以在它认为合适的时候忽略该提示,并且它可以内联 not 内联声明的函数。因此,对于现代编译器,inline只是一种服从ODR的函数,它不会隐式声明inline

更新:所以在你的情况下,函数是声明内联隐式,该定义包含在两个翻译单元(两个.cpp's)中,并且 >不由编译器内联。在这种情况下,链接器将看到该函数的符号两次,但由于内联声明,它不会抱怨多个符号。

答案 1 :(得分:1)

通过在类声明中定义函数, inline声明的函数。编译器可能无法内联它的事实(在某些情况下除外)不会改变inline声明事实。同样,你的定义在每种情况下都是相同的(除非你使用宏来改变函数的内容,例如cout在两种情况下都没有以相同的方式定义,或类似的东西)。

另一方面,如果我们在headerfile中有这样的东西:

class Base {
public:
  virtual ~Base() {}
  virtual void vfunc();
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc();
};



void Base::vfunc()
{
  {
    std::cout << "Base::vfunc()\n";
  }
}

void Derived::vfunc()
{
  {
    std::cout << "Derived::vfunc()\n";
  }
}

现在您正在破坏ODR,因为Derived::vfunc()未声明为内联,并且通过多次包含,它被定义多次(尽管具有完全相同的定义)。