আমি " ভার্চুয়াল বেস ক্লাস " কী এবং এর অর্থ কী তা জানতে চাই।
আমাকে একটি উদাহরণ দেখান:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
আমি " ভার্চুয়াল বেস ক্লাস " কী এবং এর অর্থ কী তা জানতে চাই।
আমাকে একটি উদাহরণ দেখান:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
উত্তর:
ভার্চুয়াল উত্তরাধিকারে ব্যবহৃত ভার্চুয়াল বেস ক্লাসগুলি হ'ল একাধিক উত্তরাধিকার ব্যবহারের সময় কোনও উত্তরাধিকার শ্রেণিবিন্যাসে উপস্থিত কোনও শ্রেণীর একাধিক "উদাহরণ" রোধ করার একটি উপায়।
নিম্নলিখিত পরিস্থিতিতে বিবেচনা করুন:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
উপরোক্ত শ্রেণীর শ্রেণিবিন্যাসের ফলাফল "ভয়ঙ্কর হীরা" এর ফলাফল হিসাবে দেখায়:
A
/ \
B C
\ /
D
ডি এর একটি উদাহরণ খ-এর সমন্বয়ে গঠিত হবে, যার মধ্যে এ, এবং সি রয়েছে যা এও অন্তর্ভুক্ত করে। সুতরাং আপনার দুটি "দৃষ্টান্ত" রয়েছে (আরও ভাল প্রকাশের প্রয়োজনে) এ এর।
আপনার যখন এই দৃশ্যটি দেখা যায় তখন আপনার দ্বিধাগ্রস্থ হওয়ার সম্ভাবনা থাকে। আপনি যখন এটি করেন তখন কি হয়:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
ভার্চুয়াল উত্তরাধিকার এই সমস্যাটি সমাধান করার জন্য রয়েছে। যখন আপনি আপনার ক্লাসগুলির উত্তরাধিকার সূচনা করার সময় ভার্চুয়াল নির্দিষ্ট করেন, আপনি সংকলকটি বলছেন যে আপনি কেবল একটি একক উদাহরণ চান।
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
এর অর্থ হায়ারার্কিতে অন্তর্ভুক্ত রয়েছে এ এর একটি মাত্র "উদাহরণ"। অত: পর
D d;
d.Foo(); // no longer ambiguous
এটি একটি মিনি সংক্ষিপ্তসার। আরও তথ্যের জন্য, এই এবং এটি পড়ুন । একটি ভাল উদাহরণ এখানে পাওয়া যায় ।
virtual
তবে অবজেক্ট লেআউটটি হীরার মতো দেখাচ্ছে; এবং যদি আমরা ব্যবহার করি না virtual
একটি গাছ গঠন দুই রয়েছে মত তারপর বস্তু বিন্যাস সৌন্দর্য A
গুলি
পার্শ্ব নোট হিসাবে, ড্রেডড ডায়মন্ডের সাথে সমস্যাটি হ'ল বেস ক্লাসটি একাধিকবার উপস্থিত থাকে। সুতরাং নিয়মিত উত্তরাধিকার সহ, আপনি বিশ্বাস করেন যে আপনার রয়েছে:
A
/ \
B C
\ /
D
তবে মেমরি লেআউটে আপনার রয়েছে:
A A
| |
B C
\ /
D
এটি কেন কল করার সময় আপনার D::foo()
একটি অস্পষ্টতার সমস্যা রয়েছে তা ব্যাখ্যা করে । তবে আসল সমস্যাটি তখনই আসে যখন আপনি কোনও সদস্যের পরিবর্তনশীল ব্যবহার করতে চান A
। উদাহরণস্বরূপ, আসুন আমরা বলি:
class A
{
public :
foo() ;
int m_iValue ;
} ;
আপনি অ্যাক্সেস করার চেষ্টা করব কখন m_iValue
থেকে D
, কম্পাইলার, প্রতিবাদ কারণ অনুক্রমের, এটা দুই দেখতে পাবেন হবে m_iValue
না এক। এবং যদি আপনি কোনওটি পরিবর্তন করেন তবে বলুন, B::m_iValue
(এটিই এর A::m_iValue
পিতামাতা B
), C::m_iValue
পরিবর্তিত হবে না (এটিই এর A::m_iValue
পিতামাতা C
)।
এই স্থানে ভার্চুয়াল উত্তরাধিকার কার্যকর হয়, এটির মতোই, আপনি কেবলমাত্র একটি foo()
পদ্ধতি নয় , কেবল একটি এবং একমাত্র একটি করে সত্যিকারের ডায়মন্ড লেআউটে ফিরে পাবেন m_iValue
।
কল্পনা করুন:
A
কিছু প্রাথমিক বৈশিষ্ট্য আছে।B
এটিতে এক ধরণের শীতল অ্যারে যুক্ত হয় (উদাহরণস্বরূপ)C
এতে পর্যবেক্ষক নিদর্শনগুলির মতো কিছু দুর্দান্ত বৈশিষ্ট্য যুক্ত করা হয়েছে (উদাহরণস্বরূপ, চালু m_iValue
)।D
থেকে উত্তরাধিকারী B
এবং C
, এবং এইভাবে থেকে A
।সাধারণ উত্তরাধিকারের সাথে, m_iValue
এখান থেকে পরিবর্তন D
করা অস্পষ্ট এবং এটি সমাধান করা আবশ্যক। এমনকি যদি এটি থাকে তবে m_iValues
ভিতরে দুটি রয়েছে D
, সুতরাং আপনি এটির চেয়ে ভাল মনে রাখবেন এবং একই সাথে দুটি আপডেট করবেন।
ভার্চুয়াল উত্তরাধিকার সহ, m_iValue
এখান থেকে পরিবর্তন D
করা ঠিক আছে ... তবে ... আসুন আমরা বলি যে আপনার আছে D
। এর C
ইন্টারফেসের মাধ্যমে , আপনি একটি পর্যবেক্ষক সংযুক্ত করেছেন। এবং এর B
ইন্টারফেসের মাধ্যমে আপনি শীতল অ্যারে আপডেট করুন, যা সরাসরি পরিবর্তনের পার্শ্ব প্রতিক্রিয়া m_iValue
...
m_iValue
সরাসরি পরিবর্তনটি (ভার্চুয়াল অ্যাক্সেসর পদ্ধতি ব্যবহার না করে) সম্পন্ন হওয়ার সাথে সাথে পর্যবেক্ষককে "শ্রবণ" C
বলা হবে না, কারণ শ্রবণটির বাস্তবায়নকারী কোডটি রয়েছে C
এবং B
এটি সম্পর্কে জানে না ...
যদি আপনার হায়ারার্কিতে কোনও হীরা থাকে তবে এর অর্থ হ'ল শ্রেণিবদ্ধের সাথে আপনার কোনও ভুল করার সম্ভাবনা 95% রয়েছে।
ভার্চুয়াল বেসগুলির সাথে একাধিক-উত্তরাধিকার ব্যাখ্যা করার জন্য C ++ অবজেক্টের মডেলটির জ্ঞান প্রয়োজন। এবং বিষয়টিকে স্পষ্টভাবে ব্যাখ্যা করা একটি নিবন্ধে সেরা করা হয়েছে কোনও মন্তব্য বাক্সে নয়।
এই বিষয়ে আমার সমস্ত সন্দেহের সমাধান করা সবচেয়ে ভাল, পঠনযোগ্য ব্যাখ্যাটি এই নিবন্ধটি ছিল: http://www.phpcompiler.org/articles/virtualinheritance.html
পড়ার পরে আপনাকে সত্যিকার অর্থে বিষয়টিতে অন্য কিছু পড়ার প্রয়োজন হবে না (যদি আপনি সংকলক লেখক না হয়ে থাকেন) ...
ভার্চুয়াল বেস ক্লাস হল এমন একটি ক্লাস যা তাড়িত করা যায় না: আপনি এটি থেকে সরাসরি অবজেক্ট তৈরি করতে পারবেন না।
আমি মনে করি আপনি দুটি খুব ভিন্ন জিনিস বিভ্রান্ত করছেন। ভার্চুয়াল উত্তরাধিকার বিমূর্ত শ্রেণীর মতো জিনিস নয়। ভার্চুয়াল উত্তরাধিকার ফাংশন কলগুলির আচরণকে পরিবর্তন করে; কখনও কখনও এটি ফাংশন কলগুলি সমাধান করে যে অন্যথায় দ্বিধাগ্রস্থ হবে, কখনও কখনও এটি ফাংশন কল হ্যান্ডলিংকে ব্যর্থ করে যা কোনও ভার্চুয়াল উত্তরাধিকার হিসাবে প্রত্যাশিত ব্যতীত অন্য শ্রেণীর কাছে হ্যান্ডলিং করে।
আমি ওজির ধরণের স্পষ্টিতে যুক্ত করতে চাই।
ভার্চুয়াল উত্তরাধিকার মূল্য ছাড়াই আসে না। ভার্চুয়াল সমস্ত কিছুর মতো, আপনি একটি পারফরম্যান্স হিট পাবেন। এই পারফরম্যান্স হিটের চারপাশে একটি উপায় রয়েছে যা সম্ভবত কম মার্জিত ant
ভার্চুয়ালি ডাইরেক্ট করে হীরা ভেঙে ফেলার পরিবর্তে আপনি হীরাতে আরও একটি স্তর যুক্ত করতে পারেন, এরকম কিছু পেতে:
B
/ \
D11 D12
| |
D21 D22
\ /
DD
ক্লাসগুলির কোনওটিই ভার্চুয়ালভাবে উত্তরাধিকার সূত্রে প্রাপ্ত নয়, সমস্তগুলি প্রকাশ্যে উত্তরাধিকার সূত্রে প্রাপ্ত। ক্লাস ডি 21 এবং ডি 22 এর পরে ভার্চুয়াল ফাংশন f () লুকিয়ে রাখবে যা ডিডির জন্য অস্পষ্ট, সম্ভবত ফাংশনটিকে ব্যক্তিগত বলে ঘোষণা করে। তারা প্রত্যেকে একটি মোড়ক ফাংশন যথাক্রমে f1 () এবং f2 () সংজ্ঞায়িত করত, প্রতিটি কলিং ক্লাস-লোকাল (প্রাইভেট) এফ (), এভাবে বিরোধ নিষ্পত্তি করে। শ্রেণি ডিডি f1 () কল করে যদি D11 :: f () এবং f2 () চায় তবে D12 :: f () চায়। যদি আপনি মোড়কে ইনলাইন সংজ্ঞায়িত করেন তবে আপনি সম্ভবত শূন্যের ওভারহেড পাবেন।
অবশ্যই, আপনি যদি D11 এবং D12 পরিবর্তন করতে পারেন তবে আপনি এই শ্রেণীর ভিতরে একই কৌশল করতে পারেন, তবে প্রায়শই এটি হয় না।
একাধিক এবং ভার্চুয়াল উত্তরাধিকার (গুলি) সম্পর্কে ইতিমধ্যে যা বলা হয়েছে তা ছাড়াও ড ডবসের জার্নালে একটি আকর্ষণীয় নিবন্ধ রয়েছে: একাধিক উত্তরাধিকার দরকারী হিসাবে বিবেচিত
আপনি কিছুটা বিভ্রান্ত হচ্ছেন আমি জানি না আপনি কিছু ধারণা মিশ্রিত করছেন কিনা।
আপনার ওপিতে আপনার ভার্চুয়াল বেস ক্লাস নেই। আপনার কেবল একটি বেস ক্লাস আছে।
আপনি ভার্চুয়াল উত্তরাধিকার করেছেন। এটি সাধারণত একাধিক উত্তরাধিকার হিসাবে ব্যবহৃত হয় যাতে একাধিক উত্স প্রাপ্ত শ্রেণিগুলি পুনর্গঠন ছাড়াই বেস শ্রেণির সদস্যদের ব্যবহার করে।
খাঁটি ভার্চুয়াল ফাংশন সহ একটি বেস ক্লাস তাত্ক্ষণিকভাবে করা হয় না। এর জন্য পল যে বাক্য গঠন করেছেন তা দরকার। এটি সাধারণত ব্যবহৃত হয় যাতে উদ্ভূত শ্রেণীদের অবশ্যই এই ফাংশনগুলি সংজ্ঞায়িত করতে হবে।
আমি এ সম্পর্কে আর কোনও ব্যাখ্যা দিতে চাই না কারণ আপনি যা চাইছেন তা আমি সম্পূর্ণরূপে পাই না।
এর অর্থ ভার্চুয়াল ফাংশনে একটি কল "ডান" শ্রেণিতে ফরোয়ার্ড করা হবে।
সি ++ এফএকিউ লাইট এফটিডাব্লু।
সংক্ষেপে, এটি প্রায়শই একাধিক-উত্তরাধিকারের পরিস্থিতিতে ব্যবহৃত হয়, যেখানে একটি "হীরা" শ্রেণিবদ্ধ গঠিত হয়। ভার্চুয়াল উত্তরাধিকার তখন নীচের শ্রেণিতে তৈরি অস্পষ্টতাকে ভেঙে ফেলবে, যখন আপনি class শ্রেণিতে ফাংশনটি কল করবেন এবং ফাংশনটি নীচের শ্রেণীর উপরে ডি 1 বা ডি 2 এর মধ্যে সমাধান করা দরকার। ডায়াগ্রাম এবং বিশদগুলির জন্য FAQ আইটেমটি দেখুন ।
এটি বোন প্রতিনিধিদলের ক্ষেত্রেও ব্যবহৃত হয় , এটি একটি শক্তিশালী বৈশিষ্ট্য (যদিও হৃদয়ের মূর্খতার জন্য নয়)। এই FAQ দেখুন ।
কার্যকর সি ++ তৃতীয় সংস্করণে আইটেম 40 দেখুন (দ্বিতীয় সংস্করণে 43)।
ডায়মন্ড উত্তরাধিকার চলমানযোগ্য ব্যবহারের উদাহরণ
এই উদাহরণটি দেখায় যে কীভাবে আদর্শ দৃশ্যে ভার্চুয়াল বেস শ্রেণিটি ব্যবহার করতে হয়: হীরার উত্তরাধিকার সমাধানের জন্য।
#include <cassert>
class A {
public:
A(){}
A(int i) : i(i) {}
int i;
virtual int f() = 0;
virtual int g() = 0;
virtual int h() = 0;
};
class B : public virtual A {
public:
B(int j) : j(j) {}
int j;
virtual int f() { return this->i + this->j; }
};
class C : public virtual A {
public:
C(int k) : k(k) {}
int k;
virtual int g() { return this->i + this->k; }
};
class D : public B, public C {
public:
D(int i, int j, int k) : A(i), B(j), C(k) {}
virtual int h() { return this->i + this->j + this->k; }
};
int main() {
D d = D(1, 2, 4);
assert(d.f() == 3);
assert(d.g() == 5);
assert(d.h() == 7);
}
assert(A::aDefault == 0);
মূল ফাংশন থেকে আমাকে একটি সংকলন ত্রুটি দেয়: aDefault is not a member of A
জিসিসি 5.4.0 ব্যবহার করে। মনে হয় কী করবেন?
ভার্চুয়াল ক্লাস হয় না ভার্চুয়াল উত্তরাধিকারের মতো । ভার্চুয়াল ক্লাসগুলি আপনি ইনস্ট্যান্ট করতে পারবেন না, ভার্চুয়াল উত্তরাধিকার সম্পূর্ণ অন্য কিছু।
উইকিপিডিয়া এটির চেয়ে ভাল বর্ণনা করে। http://en.wikipedia.org/wiki/Virtual_inheritance
সাধারণ 3 স্তরের নন-হীরা-নন-ভার্চুয়াল-উত্তরাধিকারের উত্তরাধিকারের সাথে, আপনি যখন কোনও নতুন সর্বাধিক প্রাপ্ত-অবজেক্টটি ইনস্ট্যান্ট করেন তখন নতুনকে ডাকা হয় এবং অবজেক্টের জন্য প্রয়োজনীয় আকারটি সংকলক দ্বারা শ্রেণীর ধরণ থেকে সমাধান করা হয় এবং নতুনতে প্রেরণ করা হয়।
নতুন একটি স্বাক্ষর আছে:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
এবং একটি কল করে malloc
অকার্যকর পয়েন্টারটি ফিরিয়ে
এরপরে এটি সর্বাধিক উত্পন্ন অবজেক্টের কনস্ট্রাক্টরের কাছে পৌঁছে দেওয়া হয়, যা অবিলম্বে মাঝারি কনস্ট্রাক্টরকে কল করবে এবং তারপরে মাঝারি কনস্ট্রাক্টর অবিলম্বে বেস কনস্ট্রাক্টরকে কল করবে। এরপরে বেসটি বস্তুর শুরুতে তার ভার্চুয়াল টেবিলের জন্য একটি পয়েন্টার সঞ্চয় করে এবং তারপরে এর এর বৈশিষ্ট্যগুলি। এরপরে মধ্যবর্তী নির্মাণকারীর কাছে ফিরে আসে যা একই ভার্চুয়াল টেবিল পয়েন্টার একই স্থানে সংরক্ষণ করবে এবং তারপরে বৈশিষ্ট্যগুলির পরে এর বৈশিষ্ট্যগুলি যা বেস কনস্ট্রাক্টর দ্বারা সংরক্ষণ করা হত। এটি সর্বাধিক উত্স প্রাপ্ত কন্সট্রাক্টরের কাছে ফিরে আসে, যা একই ভার্চুয়াল টেবিলের জন্য একটি পয়েন্টার একই স্থানে সংরক্ষণ করে এবং তারপরে বৈশিষ্ট্যগুলির পরে এর বৈশিষ্ট্যগুলি মধ্যবর্তী নির্মাণকারী দ্বারা সংরক্ষণ করা হত।
ভার্চুয়াল টেবিল পয়েন্টারটি ওভাররাইট করা হয়েছে বলে ভার্চুয়াল টেবিল পয়েন্টারটি সর্বদা সর্বাধিক উত্স প্রাপ্ত শ্রেণীর এক হয়ে যায়। ভার্চুয়ালিটি সর্বাধিক উদ্ভূত শ্রেণীর দিকে প্রচার করে সুতরাং যদি কোনও ফাংশন মধ্যবিত্তে ভার্চুয়াল হয় তবে এটি সবচেয়ে উত্পন্ন শ্রেণিতে ভার্চুয়াল হবে তবে বেস শ্রেণীর নয়। আপনি যদি বহিরাগতভাবে সবচেয়ে উত্সযুক্ত শ্রেণীর উদাহরণটি বেস শ্রেণীর দিকে নির্দেশকের কাছে ফেলে দেন তবে সংকলক এটি ভার্চুয়াল টেবিলের পরোক্ষ কলটিতে সমাধান করবে না এবং পরিবর্তে সরাসরি ফাংশনটিতে কল করবে A::function()
। কোনও ফাংশন যদি আপনি এটিকে ফেলে দিয়েছেন সেটির জন্য ভার্চুয়াল হয় তবে এটি ভার্চুয়াল টেবিলের মধ্যে কল করার সমাধান করবে যা সর্বদা সর্বাধিক উত্সযুক্ত শ্রেণীর হবে। যদি এই ধরণের জন্য এটি ভার্চুয়াল না হয় তবে এটি কেবল কল করবেType::function()
করে এটিতে বস্তুর পয়েন্টারটি প্রেরণ করবে, টাইপ করুন cast
আসলে যখন আমি এর ভার্চুয়াল টেবিলের দিকে পয়েন্টার বলি, এটি প্রকৃতপক্ষে ভার্চুয়াল টেবিলের মধ্যে সর্বদা 16 এর একটি অফসেট।
vtable for Base:
.quad 0
.quad typeinfo for Base
.quad Base::CommonFunction()
.quad Base::VirtualFunction()
pointer is typically to the first function i.e.
mov edx, OFFSET FLAT:vtable for Base+16
virtual
যদি কম-উত্পন্ন ক্লাসে ভার্চুয়াল হয় তবে এটি আরও প্রচারিত ক্লাসগুলিতে আবার প্রয়োজন হয় না কারণ এটি প্রচার করে। তবে এটি প্রদর্শন করতে ব্যবহৃত হতে পারে যে ফাংশনটি প্রকৃতপক্ষে একটি ভার্চুয়াল ফাংশন, ক্লাসগুলি পরীক্ষা না করে এটির ধরণের সংজ্ঞাটি উত্তরাধিকার সূত্রে প্রাপ্ত হয়।
override
আরেকটি সংকলক প্রহরী যা বলছে যে এই ফাংশনটি কোনও কিছুকে ওভাররাইড করছে এবং যদি তা না হয় তবে একটি সংকলক ত্রুটি ছুঁড়ে দেয়।
= 0
এর অর্থ এটি একটি বিমূর্ত ফাংশন
final
আরও উদ্ভূত শ্রেণিতে আবার ভার্চুয়াল ফাংশন বাস্তবায়িত হতে বাধা দেয় এবং সর্বাধিক উত্পন্ন শ্রেণীর ভার্চুয়াল টেবিলটি সেই শ্রেণীর চূড়ান্ত ফাংশন রয়েছে কিনা তা নিশ্চিত করবে।
= default
এটি ডকুমেন্টেশনে স্পষ্ট করে তোলে যে সংকলকটি ডিফল্ট বাস্তবায়ন ব্যবহার করবে
= delete
এটির জন্য যদি কল করার চেষ্টা করা হয় তবে একটি সংকলক ত্রুটি দিন
বিবেচনা
class Base
{
int a = 1;
int b = 2;
public:
void virtual CommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
class DerivedClass1: virtual public Base
{
int c = 3;
public:
void virtual DerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
class DerivedClass2 : virtual public Base
{
int d = 4;
public:
//void virtual DerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
void virtual DerivedCommonFunction2(){} ;
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e = 5;
public:
void virtual DerivedDerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
int main () {
DerivedDerivedClass* d = new DerivedDerivedClass;
d->VirtualFunction();
d->DerivedCommonFunction();
d->DerivedCommonFunction2();
d->DerivedDerivedCommonFunction();
((DerivedClass2*)d)->DerivedCommonFunction2();
((Base*)d)->VirtualFunction();
}
কার্যত বেস ক্লাস উত্তরাধিকার সূত্রে না পেয়ে আপনি এমন একটি বস্তু পাবেন যা দেখতে এরকম দেখাচ্ছে:
এর পরিবর্তে:
অর্থাৎ 2 বেস অবজেক্ট থাকবে।
উপরের ভার্চুয়াল ডায়মন্ডের উত্তরাধিকারের পরিস্থিতিতে, নতুন বলা হওয়ার পরে, এটি সর্বাধিক উত্স প্রাপ্ত কন্সট্রাক্টরকে কল করে এবং সেই নির্মাত্রে, এটি কল করে কলিংয়ের পরিবর্তে, সমস্ত ভারিচুয়াল কনস্ট্রাক্টরকে তার ভার্চুয়াল টেবিল টেবিলে অফসেটগুলি পাস করার আহ্বান জানায় DerivedClass1::DerivedClass1()
andDerivedClass2::DerivedClass2()
এবং তারপর যারা উভয় কলিংBase::Base()
নিম্নলিখিতগুলি ডিবাগ মোড -O0 তে সংকলিত রয়েছে যাতে অনর্থক সমাবেশ হবে
main:
.LFB8:
push rbp
mov rbp, rsp
push rbx
sub rsp, 24
mov edi, 48 //pass size to new
call operator new(unsigned long) //call new
mov rbx, rax //move the address of the allocation to rbx
mov rdi, rbx //move it to rdi i.e. pass to the call
call DerivedDerivedClass::DerivedDerivedClass() [complete object constructor] //construct on this address
mov QWORD PTR [rbp-24], rbx //store the address of the object on the stack as d
DerivedDerivedClass::DerivedDerivedClass() [complete object constructor]:
.LFB20:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
.LBB5:
mov rax, QWORD PTR [rbp-8] // object address now in rax
add rax, 32 //increment address by 32
mov rdi, rax // move object address+32 to rdi i.e. pass to call
call Base::Base() [base object constructor]
mov rax, QWORD PTR [rbp-8] //move object address to rax
mov edx, OFFSET FLAT:VTT for DerivedDerivedClass+8 //move address of VTT+8 to edx
mov rsi, rdx //pass VTT+8 address as 2nd parameter
mov rdi, rax //object address as first
call DerivedClass1::DerivedClass1() [base object constructor]
mov rax, QWORD PTR [rbp-8] //move object address to rax
add rax, 16 //increment object address by 16
mov edx, OFFSET FLAT:VTT for DerivedDerivedClass+24 //store address of VTT+24 in edx
mov rsi, rdx //pass address of VTT+24 as second parameter
mov rdi, rax //address of object as first
call DerivedClass2::DerivedClass2() [base object constructor]
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+24 //move this to edx
mov rax, QWORD PTR [rbp-8] // object address now in rax
mov QWORD PTR [rax], rdx. //store address of vtable for DerivedDerivedClass+24 at the start of the object
mov rax, QWORD PTR [rbp-8] // object address now in rax
add rax, 32 // increment object address by 32
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+120 //move this to edx
mov QWORD PTR [rax], rdx //store vtable for DerivedDerivedClass+120 at object+32 (Base)
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+72 //store this in edx
mov rax, QWORD PTR [rbp-8] //move object address to rax
mov QWORD PTR [rax+16], rdx //store vtable for DerivedDerivedClass+72 at object+16 (DerivedClass2)
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax+28], 5
.LBE5:
nop
leave
ret
এটি Base::Base()
32 টি অফসেট অবজেক্টটিতে একটি পয়েন্টার দিয়ে কল করে Base
Base::Base() [base object constructor]:
.LFB11:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi //stores address of object on stack (-O0)
.LBB2:
mov edx, OFFSET FLAT:vtable for Base+16 //puts vtable for Base+16 in edx
mov rax, QWORD PTR [rbp-8] //copies address of object from stack to rax
mov QWORD PTR [rax], rdx //stores it address of object
mov rax, QWORD PTR [rbp-8] //copies address of object on stack to rax again
mov DWORD PTR [rax+8], 1 //stores a = 1 in the object
mov rax, QWORD PTR [rbp-8] //junk from -O0
mov DWORD PTR [rax+12], 2 //stores b = 2 in the object
.LBE2:
nop
pop rbp
ret
DerivedDerivedClass::DerivedDerivedClass()
তারপরে DerivedClass1::DerivedClass1()
0 টি অফসেটটিতে পয়েন্টার সহ কল করে এবং এর ঠিকানাটিও পাস করেVTT for DerivedDerivedClass+8
DerivedClass1::DerivedClass1() [base object constructor]:
.LFB14:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi //address of object
mov QWORD PTR [rbp-16], rsi //address of VTT+8
.LBB3:
mov rax, QWORD PTR [rbp-16] //address of VTT+8 now in rax
mov rdx, QWORD PTR [rax] //address of DerivedClass1-in-DerivedDerivedClass+24 now in rdx
mov rax, QWORD PTR [rbp-8] //address of object now in rax
mov QWORD PTR [rax], rdx //store address of DerivedClass1-in-.. in the object
mov rax, QWORD PTR [rbp-8] // address of object now in rax
mov rax, QWORD PTR [rax] //address of DerivedClass1-in.. now implicitly in rax
sub rax, 24 //address of DerivedClass1-in-DerivedDerivedClass+0 now in rax
mov rax, QWORD PTR [rax] //value of 32 now in rax
mov rdx, rax // now in rdx
mov rax, QWORD PTR [rbp-8] //address of object now in rax
add rdx, rax //address of object+32 now in rdx
mov rax, QWORD PTR [rbp-16] //address of VTT+8 now in rax
mov rax, QWORD PTR [rax+8] //address of DerivedClass1-in-DerivedDerivedClass+72 (Base::CommonFunction()) now in rax
mov QWORD PTR [rdx], rax //store at address object+32 (offset to Base)
mov rax, QWORD PTR [rbp-8] //store address of object in rax, return
mov DWORD PTR [rax+8], 3 //store its attribute c = 3 in the object
.LBE3:
nop
pop rbp
ret
VTT for DerivedDerivedClass:
.quad vtable for DerivedDerivedClass+24
.quad construction vtable for DerivedClass1-in-DerivedDerivedClass+24
.quad construction vtable for DerivedClass1-in-DerivedDerivedClass+72
.quad construction vtable for DerivedClass2-in-DerivedDerivedClass+24
.quad construction vtable for DerivedClass2-in-DerivedDerivedClass+72
.quad vtable for DerivedDerivedClass+120
.quad vtable for DerivedDerivedClass+72
construction vtable for DerivedClass1-in-DerivedDerivedClass:
.quad 32
.quad 0
.quad typeinfo for DerivedClass1
.quad DerivedClass1::DerivedCommonFunction()
.quad DerivedClass1::VirtualFunction()
.quad -32
.quad 0
.quad -32
.quad typeinfo for DerivedClass1
.quad Base::CommonFunction()
.quad virtual thunk to DerivedClass1::VirtualFunction()
construction vtable for DerivedClass2-in-DerivedDerivedClass:
.quad 16
.quad 0
.quad typeinfo for DerivedClass2
.quad DerivedClass2::VirtualFunction()
.quad DerivedClass2::DerivedCommonFunction2()
.quad -16
.quad 0
.quad -16
.quad typeinfo for DerivedClass2
.quad Base::CommonFunction()
.quad virtual thunk to DerivedClass2::VirtualFunction()
vtable for DerivedDerivedClass:
.quad 32
.quad 0
.quad typeinfo for DerivedDerivedClass
.quad DerivedClass1::DerivedCommonFunction()
.quad DerivedDerivedClass::VirtualFunction()
.quad DerivedDerivedClass::DerivedDerivedCommonFunction()
.quad 16
.quad -16
.quad typeinfo for DerivedDerivedClass
.quad non-virtual thunk to DerivedDerivedClass::VirtualFunction()
.quad DerivedClass2::DerivedCommonFunction2()
.quad -32
.quad 0
.quad -32
.quad typeinfo for DerivedDerivedClass
.quad Base::CommonFunction()
.quad virtual thunk to DerivedDerivedClass::VirtualFunction()
virtual thunk to DerivedClass1::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK0
virtual thunk to DerivedClass2::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK1
virtual thunk to DerivedDerivedClass::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK2
non-virtual thunk to DerivedDerivedClass::VirtualFunction():
sub rdi, 16
jmp .LTHUNK3
.set .LTHUNK0,DerivedClass1::VirtualFunction()
.set .LTHUNK1,DerivedClass2::VirtualFunction()
.set .LTHUNK2,DerivedDerivedClass::VirtualFunction()
.set .LTHUNK3,DerivedDerivedClass::VirtualFunction()
DerivedDerivedClass::DerivedDerivedClass()
তারপর বস্তু + + 16 এর ঠিকানা এবং জন্য VTT এর ঠিকানা পাস DerivedDerivedClass+24
করার DerivedClass2::DerivedClass2()
যার সমাবেশ অভিন্ন DerivedClass1::DerivedClass1()
লাইন ছাড়া mov DWORD PTR [rax+8], 3
স্পষ্টত একটি 4 পরিবর্তে 3 যা d = 4
।
এর পরে, এটি DerivedDerivedClass
the শ্রেণীর জন্য উপস্থাপনের vtable এর পয়েন্টারগুলিতে পয়েন্টারগুলির সাথে অবজেক্টের সমস্ত 3 ভার্চুয়াল টেবিল পয়েন্টারকে প্রতিস্থাপন করে ।
d->VirtualFunction();
:
mov rax, QWORD PTR [rbp-24] //store pointer to virtual table in rax
mov rax, QWORD PTR [rax] //dereference and store in rax
add rax, 8 // call the 2nd function in the table
mov rdx, QWORD PTR [rax] //dereference
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call rdx
d->DerivedCommonFunction();
:
mov rax, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rdx]
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx
d->DerivedCommonFunction2();
:
mov rax, QWORD PTR [rbp-24]
lea rdx, [rax+16]
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax+16]
add rax, 8
mov rax, QWORD PTR [rax]
mov rdi, rdx
call rax
d->DerivedDerivedCommonFunction();
:
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax]
add rax, 16
mov rdx, QWORD PTR [rax]
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call rdx
((DerivedClass2*)d)->DerivedCommonFunction2();
:
cmp QWORD PTR [rbp-24], 0
je .L14
mov rax, QWORD PTR [rbp-24]
add rax, 16
jmp .L15
.L14:
mov eax, 0
.L15:
cmp QWORD PTR [rbp-24], 0
cmp QWORD PTR [rbp-24], 0
je .L18
mov rdx, QWORD PTR [rbp-24]
add rdx, 16
jmp .L19
.L18:
mov edx, 0
.L19:
mov rdx, QWORD PTR [rdx]
add rdx, 8
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx
((Base*)d)->VirtualFunction();
:
cmp QWORD PTR [rbp-24], 0
je .L20
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax]
sub rax, 24
mov rax, QWORD PTR [rax]
mov rdx, rax
mov rax, QWORD PTR [rbp-24]
add rax, rdx
jmp .L21
.L20:
mov eax, 0
.L21:
cmp QWORD PTR [rbp-24], 0
cmp QWORD PTR [rbp-24], 0
je .L24
mov rdx, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rdx]
sub rdx, 24
mov rdx, QWORD PTR [rdx]
mov rcx, rdx
mov rdx, QWORD PTR [rbp-24]
add rdx, rcx
jmp .L25
.L24:
mov edx, 0
.L25:
mov rdx, QWORD PTR [rdx]
add rdx, 8
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx