पृष्ठभूमि
हाल ही में, मेरा एक सहयोगी एक समस्या है, जहां एक पुस्तकालय के लिए एक हेडर फाइल के एक पुराने संस्करण इस्तेमाल किया गया था में भाग गया। नतीजा यह था कि वर्चुअल फ़ंक्शन को कॉल करने के लिए जेनरेट किया गया कोड C++ में वर्चुअल फ़ंक्शन लुकअप टेबल में गलत ऑफ़सेट को संदर्भित करता है (vtable)।संकलन समय स्थिर प्रकार की जांच ++
दुर्भाग्यवश, यह त्रुटि संकलन के दौरान पकड़ी नहीं गई थी।
प्रश्न
सभी साधारण कार्यों उनके घायल नाम जो सही समारोह (सही अधिभार संस्करण सहित) को सुनिश्चित करती जुड़े हुए हैं लिंकर द्वारा चुना जाता है। इसी तरह, कोई कल्पना कर सकता है कि एक ऑब्जेक्ट फ़ाइल या लाइब्रेरी में C12+ वर्ग के लिए vtable में फ़ंक्शंस के बारे में प्रतीकात्मक जानकारी हो सकती है।
क्या सी ++ कंपाइलर (g++
या विजुअल स्टूडियो कहें) को लिंक करने के दौरान वर्चुअल फ़ंक्शन पर चेक कॉल करने का कोई तरीका है?
उदाहरण
यहां एक साधारण परीक्षण उदाहरण है।
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()
एक ही है हस्ताक्षर इसलिए गलत होने वाली "केवल" चीज गलत कार्य है जिसे बुलाया जा रहा है (जो स्वयं में बुरा है और पूरी तरह से ध्यान नहीं दिया जा सकता है), लेकिन यदि हस्ताक्षर अलग हैं तो यह (और देखा गया है) भ्रष्टाचार को ढंकने के लिए नेतृत्व कर सकता है - उदाहरण के लिए, जब विंडोज़ पर एक डीएलएल में एक फ़ंक्शन को कॉल करते हैं जहां पास्कल कॉलिंग कन्वेंशन का उपयोग किया जाता है (कॉलर तर्क देता है और कैली वापस लौटने से पहले पॉप करता है)।
ओडीआर उल्लंघन आम तौर पर निदान नहीं हैं। यही कारण है कि मानक को उनके लिए निदान की आवश्यकता नहीं होती है, और यही कारण है कि वे इतनी भयानक विफलता मोड हैं। –
आपके शामिल गार्ड अवैध हैं। http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier –
@BaummitAugen फिक्स्ड। – mgd