"一个定义规则"规定:
在整个程序中,一个对象 或者非内联函数不能有多个定义。(来自 维基百科(
好吧,我知道如果在头文件中定义了成员函数,它是隐式内联的,并且 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,不是吗?它是如何编译的(链接(?
我刚刚看到:
多个定义
在某些情况下,一个类型或 模板。由多个头文件和源组成的程序 文件通常具有多个类型定义,但不会 每个翻译单元有多个定义。如果程序包含 一个类型的多个定义,则每个定义必须是 等效(也取自维基百科(
有哪些certain cases
?上述情况是否是其中之一?
vfunc(和dtor(在foo.cpp和main中都被调用.cpp多态(不是内联(,这意味着它在整个程序中被定义两次,
首先:召唤并不意味着定义。因此,函数调用不会告诉您是否违反了 ODR。
所以它违反了 ODR,不是吗?它是如何编译的(链接(?
它编译,因为在类定义中定义的成员函数是内联隐式声明的,因此不违反 ODR。这适用于vfunc
定义以及 dtor,所以你在这里很好。
注意:
声明为inline
(显式或隐式(的函数与实际内联的函数之间存在差异。编译器决定内联一次函数可能会受到inline
关键字的影响,但这过去和现在都只是一个提示。如今,优化器可以比任何人更好地预测何时以及何时不内联是一个不错的选择,因此编译器可以在它认为合适的时间忽略该提示,并且可以内联尚未声明为内联的函数。因此,对于现代编译器,inline
只是服从函数 ODR 的一种手段,这些函数不是隐式inline
声明的。
更新:因此,在您的情况下,函数是内联隐式声明的,定义包含在两个翻译单元(两个.cpp(中,并且编译器不会内联。在这种情况下,链接器将看到函数的符号两次,但它不会因为内联声明而抱怨多个符号。
通过在类声明中定义函数,它是一个inline
声明的函数。编译器可能无法内联它的事实(在某些情况下除外(不会更改inline
声明事实。同样,您的定义在每种情况下都是相同的(除非您使用宏来更改函数的内容,例如 cout
在这两种情况下的定义方式不同,或者类似的东西(。
另一方面,如果我们在头文件中有这样的东西:
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()
不是内联声明的,并且通过多次包含,它被多次定义(尽管定义完全相同(。