[英]Compile-time static type check of virtual functions in C++
最近,我的一位同事遇到了一個問題,其中使用了舊版本的庫頭文件。 結果是,為在C ++中調用虛函數而生成的代碼引用了類( vtable )的虛函數查找表中的錯誤偏移量。
不幸的是,在編譯期間沒有捕獲到此錯誤。
所有普通函數都使用其錯位名稱進行鏈接,以確保鏈接器選擇正確的函數(包括正確的重載變量)。 同樣,可以想象一個目標文件或庫可以包含有關Ctable類的vtable中的函數的符號信息。
有沒有辦法讓C ++編譯器(比如g++
或Visual Studio)在鏈接期間檢查對虛函數的調用?
這是一個簡單的測試示例。 想象一下這個簡單的頭文件和相關的實現:
Base.hpp:
#ifndef BASE_HPP
#define BASE_HPP
namespace Test
{
class Base
{
public:
virtual int f() const = 0;
virtual int g() const = 0;
virtual int h() const = 0;
};
class BaseFactory
{
public:
static const Base* createBase();
};
}
#endif
Derived.cpp:
#include "Base.hpp"
#include <iostream>
using namespace std;
namespace Test
{
class Derived : public Base
{
public:
virtual int f() const
{
cout << "Derived::f()" << endl;
return 1;
}
virtual int g() const
{
cout << "Derived::g()" << endl;
return 2;
}
virtual int h() const
{
cout << "Derived::h()" << endl;
return 3;
}
};
const Base* BaseFactory::createBase()
{
return new Derived();
}
}
現在,假設一個程序使用了錯誤/舊版本的頭文件,其中缺少中間的虛函數:
BaseWrong.hpp
#ifndef BASEWRONG_HPP
#define BASEWRONG_HPP
namespace Test
{
class Base
{
public:
virtual int f() const = 0;
// Missing: virtual int g() const = 0;
virtual int h() const = 0;
};
class BaseFactory
{
public:
static const Base* createBase();
};
}
#endif
所以這里我們有主程序:
Main.cpp的
// Including the _wrong_ version of the header!
#include "BaseWrong.hpp"
#include <iostream>
using namespace std;
int main()
{
const Test::Base* base = Test::BaseFactory::createBase();
const int fres = base->f();
cout << "f() returned: " << fres << endl;
const int hres = base->h();
cout << "h() returned: " << hres << endl;
return 0;
}
當我們使用正確的頭編譯“庫”,然后使用錯誤的頭編譯和鏈接主程序...
$ g++ -c Derived.cpp
$ g++ Main.cpp Derived.o -o Main
...然后對h()
的虛擬調用在vtable中使用了錯誤的索引,因此調用實際上轉到g()
:
$ ./Main
Derived::f()
f() returned: 1
Derived::g()
h() returned: 2
在這個小例子中,函數g()
和h()
具有相同的簽名,因此出錯的“唯一”的東西是被調用的錯誤函數(這本身很糟糕並且可能完全被忽視),但如果簽名是不同的這可以(並且已經看到)導致堆棧損壞 - 例如,在使用Pascal調用約定的Windows上調用DLL中的函數時(調用者推送參數並且callee在返回之前彈出它們)。
對你的問題的簡短回答是否定的。 基本問題是在編譯時計算被調用函數的偏移量; 因此,如果您的調用者代碼是使用包含“virtual int g()const”的(不正確的)頭文件編譯的,那么您的main.o將通過g()的存在對h()的所有引用進行偏移。 但是您的庫已經使用正確的頭文件進行編譯,因此沒有函數g(),因此Derived.o中h()的偏移量將與main.o中的偏移量不同。
這不是對虛函數進行類型調用的問題 - 這是基於C ++編譯器執行編譯時函數偏移計算而非運行時的事實的“限制”。
您可以通過使用dl_open而不是直接函數調用並動態鏈接庫來解決此問題,而不是靜態鏈接它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.