সি ++ এ, ভার্চুয়াল বেস ক্লাসটি কী?


403

আমি " ভার্চুয়াল বেস ক্লাস " কী এবং এর অর্থ কী তা জানতে চাই।

আমাকে একটি উদাহরণ দেখান:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};

আমাদের কি 'একাধিক উত্তরাধিকারে' ভার্চুয়াল বেস ক্লাস ব্যবহার করা উচিত কারণ ক্লাস এ এর ​​সদস্য ভেরিয়েবল ইন্ট এবং ক্লাস বি এর সদস্য ইন্ট এ এবং ক্লাস সি ক্লাস এ এবং বি উত্তরাধিকার সূত্রে প্রাপ্ত হয় কীভাবে আমরা কোন 'ক' ব্যবহার করব তা কীভাবে সিদ্ধান্ত নেব?
নমিত সিনহা

2
@ নমিতসিংহ না, ভার্চুয়াল উত্তরাধিকার সেই সমস্যার সমাধান করে না । সদস্যটি যে কোনওভাবেই অস্পষ্ট হতে পারে
ইছাতিও

@ নমিতসিংহ ভার্চুয়াল উত্তরাধিকার একাধিক উত্তরাধিকার সম্পর্কিত অস্পষ্টতা অপসারণ করার যাদুকরী সরঞ্জাম নয়। এটি একের অধিক পরোক্ষ বেস থাকার "একটি" সমস্যা "সমাধান করে"। যা কেবল তখনই সমস্যা হয় যদি এটি ভাগ করে নেওয়ার উদ্দেশ্যে করা হত (প্রায়শই তবে সর্বদা ক্ষেত্রে হয় না)।
কৌতূহলী

উত্তর:


533

ভার্চুয়াল উত্তরাধিকারে ব্যবহৃত ভার্চুয়াল বেস ক্লাসগুলি হ'ল একাধিক উত্তরাধিকার ব্যবহারের সময় কোনও উত্তরাধিকার শ্রেণিবিন্যাসে উপস্থিত কোনও শ্রেণীর একাধিক "উদাহরণ" রোধ করার একটি উপায়।

নিম্নলিখিত পরিস্থিতিতে বিবেচনা করুন:

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

এটি একটি মিনি সংক্ষিপ্তসার। আরও তথ্যের জন্য, এই এবং এটি পড়ুন । একটি ভাল উদাহরণ এখানে পাওয়া যায়


7
@ বোহদান না এটি করেনা :)
ওজে।

6
@OJ। কেন না? তারা হাসিখুশি :)
বোহদান

15
@ বোহদান ভার্চুয়াল কীওয়ার্ডটি যত কম ব্যবহার করুন, কারণ যখন আমরা ভার্চুয়াল কীওয়ার্ড ব্যবহার করি তখন একটি ভারী ওজন প্রক্রিয়া প্রয়োগ করা হয়। সুতরাং, আপনার প্রোগ্রাম দক্ষতা হ্রাস হবে।
সাগর 14

73
আপনার "ভয়ঙ্কর হীরা" চিত্রটি বিভ্রান্তিকর, যদিও এটি সাধারণত ব্যবহৃত হয় বলে মনে হয়। - এটি আসলে বর্গ উত্তরাধিকার সম্পর্ক দেখাচ্ছে একটি ডায়াগ্রাম হয় না একটি বস্তু বিন্যাস। বিভ্রান্তিকর অংশটি হ'ল আমরা যদি ব্যবহার করি virtualতবে অবজেক্ট লেআউটটি হীরার মতো দেখাচ্ছে; এবং যদি আমরা ব্যবহার করি না virtualএকটি গাছ গঠন দুই রয়েছে মত তারপর বস্তু বিন্যাস সৌন্দর্য Aগুলি
এম এম

5
এমএম দ্বারা বর্ণিত কারণের জন্য আমাকে এই উত্তরটি হ্রাস করতে হবে - ডায়াগ্রামটি পোস্টের বিপরীতে প্রকাশ করে।
ডেভিড স্টোন

251

স্মৃতি বিন্যাস সম্পর্কে

পার্শ্ব নোট হিসাবে, ড্রেডড ডায়মন্ডের সাথে সমস্যাটি হ'ল বেস ক্লাসটি একাধিকবার উপস্থিত থাকে। সুতরাং নিয়মিত উত্তরাধিকার সহ, আপনি বিশ্বাস করেন যে আপনার রয়েছে:

  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% রয়েছে।


আপনার 'কী ভুল হতে পারে' তা বেস সদস্যের সরাসরি অ্যাক্সেসের কারণে, একাধিক উত্তরাধিকারের কারণে নয়। পরিত্রাণ পান 'বি' এবং আপনি একই সমস্যা আছে বেসিক নিয়ম:। 'তার ব্যক্তিগত নয়, এটা ভার্চুয়াল হওয়া উচিত' সমস্যা এড়াতে m_iValue ভার্চুয়াল নয় এবং তজ্জন্য ব্যক্তিগত হওয়া উচিত।
ক্রিস ডড

4
@ ক্রিস ডড: ঠিক নয়। এম_আইভ্যালিউয়ের সাথে যা ঘটে তা কোনও চিহ্নের ( যেমন টাইপিডেফ , সদস্য ভেরিয়েবল, সদস্য ফাংশন, বেস ক্লাসে কাস্ট করা ইত্যাদি ) ঘটত । এটি সত্যিই একাধিক উত্তরাধিকারের সমস্যা, এমন একটি সমস্যা যা ব্যবহারকারীদের জাভা পথে যাওয়ার পরিবর্তে একাধিক উত্তরাধিকার সঠিকভাবে ব্যবহার করতে সচেতন হওয়া উচিত এবং সিদ্ধান্ত নেওয়া উচিত "একাধিক উত্তরাধিকার 100% মন্দ, এটি ইন্টারফেসের মাধ্যমে করা যাক"।
পেরসেবল

হাই, যখন আমরা ভার্চুয়াল কীওয়ার্ডটি ব্যবহার করি, তখন এ এর ​​একটি মাত্র অনুলিপি থাকবে আমার প্রশ্ন হ'ল আমরা কীভাবে জানব যে এটি বি বা সি থেকে আসছে কিনা? আমার প্রশ্ন কি আদৌ বৈধ?
ব্যবহারকারীর 875036

@ ব্যবহারকারী 5050৫০5036: এ বি এবং সি উভয় থেকেই আসছেন, প্রকৃতপক্ষে, ভার্চুয়ালটি কয়েকটি জিনিস পরিবর্তন করে (উদাহরণস্বরূপ, ডি, বি বা সি নয়, এ এর ​​নির্মাণকারীকে কল করবে)। বি এবং সি (এবং ডি)
উভয়েরই

3
এফডাব্লুআইডাব্লু, কারও অবাক হওয়ার ক্ষেত্রে সদস্য ভেরিয়েবলগুলি ভার্চুয়াল হতে পারে না - ভার্চুয়াল ফাংশনগুলির জন্য নির্দিষ্টকরণকারী । তাই রেফারেন্স: stackoverflow.com/questions/3698831/...
rholmes

34

ভার্চুয়াল বেসগুলির সাথে একাধিক-উত্তরাধিকার ব্যাখ্যা করার জন্য C ++ অবজেক্টের মডেলটির জ্ঞান প্রয়োজন। এবং বিষয়টিকে স্পষ্টভাবে ব্যাখ্যা করা একটি নিবন্ধে সেরা করা হয়েছে কোনও মন্তব্য বাক্সে নয়।

এই বিষয়ে আমার সমস্ত সন্দেহের সমাধান করা সবচেয়ে ভাল, পঠনযোগ্য ব্যাখ্যাটি এই নিবন্ধটি ছিল: http://www.phpcompiler.org/articles/virtualinheritance.html

পড়ার পরে আপনাকে সত্যিকার অর্থে বিষয়টিতে অন্য কিছু পড়ার প্রয়োজন হবে না (যদি আপনি সংকলক লেখক না হয়ে থাকেন) ...


10

ভার্চুয়াল বেস ক্লাস হল এমন একটি ক্লাস যা তাড়িত করা যায় না: আপনি এটি থেকে সরাসরি অবজেক্ট তৈরি করতে পারবেন না।

আমি মনে করি আপনি দুটি খুব ভিন্ন জিনিস বিভ্রান্ত করছেন। ভার্চুয়াল উত্তরাধিকার বিমূর্ত শ্রেণীর মতো জিনিস নয়। ভার্চুয়াল উত্তরাধিকার ফাংশন কলগুলির আচরণকে পরিবর্তন করে; কখনও কখনও এটি ফাংশন কলগুলি সমাধান করে যে অন্যথায় দ্বিধাগ্রস্থ হবে, কখনও কখনও এটি ফাংশন কল হ্যান্ডলিংকে ব্যর্থ করে যা কোনও ভার্চুয়াল উত্তরাধিকার হিসাবে প্রত্যাশিত ব্যতীত অন্য শ্রেণীর কাছে হ্যান্ডলিং করে।


7

আমি ওজির ধরণের স্পষ্টিতে যুক্ত করতে চাই।

ভার্চুয়াল উত্তরাধিকার মূল্য ছাড়াই আসে না। ভার্চুয়াল সমস্ত কিছুর মতো, আপনি একটি পারফরম্যান্স হিট পাবেন। এই পারফরম্যান্স হিটের চারপাশে একটি উপায় রয়েছে যা সম্ভবত কম মার্জিত ant

ভার্চুয়ালি ডাইরেক্ট করে হীরা ভেঙে ফেলার পরিবর্তে আপনি হীরাতে আরও একটি স্তর যুক্ত করতে পারেন, এরকম কিছু পেতে:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

ক্লাসগুলির কোনওটিই ভার্চুয়ালভাবে উত্তরাধিকার সূত্রে প্রাপ্ত নয়, সমস্তগুলি প্রকাশ্যে উত্তরাধিকার সূত্রে প্রাপ্ত। ক্লাস ডি 21 এবং ডি 22 এর পরে ভার্চুয়াল ফাংশন f () লুকিয়ে রাখবে যা ডিডির জন্য অস্পষ্ট, সম্ভবত ফাংশনটিকে ব্যক্তিগত বলে ঘোষণা করে। তারা প্রত্যেকে একটি মোড়ক ফাংশন যথাক্রমে f1 () এবং f2 () সংজ্ঞায়িত করত, প্রতিটি কলিং ক্লাস-লোকাল (প্রাইভেট) এফ (), এভাবে বিরোধ নিষ্পত্তি করে। শ্রেণি ডিডি f1 () কল করে যদি D11 :: f () এবং f2 () চায় তবে D12 :: f () চায়। যদি আপনি মোড়কে ইনলাইন সংজ্ঞায়িত করেন তবে আপনি সম্ভবত শূন্যের ওভারহেড পাবেন।

অবশ্যই, আপনি যদি D11 এবং D12 পরিবর্তন করতে পারেন তবে আপনি এই শ্রেণীর ভিতরে একই কৌশল করতে পারেন, তবে প্রায়শই এটি হয় না।


2
এটি কম-বেশি মার্জিত বা অস্পষ্টতাগুলি সমাধান করার বিষয় নয় (এর জন্য আপনি সর্বদা স্পষ্ট এক্সএক্সএক্সএক্স :: বিশেষ উল্লেখগুলি ব্যবহার করতে পারেন)। অ-ভার্চুয়াল উত্তরাধিকারের সাথে, ক্লাস ডিডির প্রতিটি ঘটনার বি.এর দুটি স্বতন্ত্র দৃষ্টান্ত রয়েছে যেমন ক্লাসের একক অ-স্থিতিশীল ডেটা সদস্য হওয়ার সাথে সাথে ভার্চুয়াল এবং নন-ভার্চুয়াল উত্তরাধিকার কেবল বাক্যবিন্যাসের চেয়ে আলাদা হয়।
ব্যবহারকারী 3489112

@ ব্যবহারকারী 3489112 যত তাড়াতাড়ি ... কিছুই নেই। ভার্চুয়াল এবং নন ভার্চুয়াল উত্তরাধিকার সময়কালীন শব্দার্থভাবে পৃথক।
কৌতূহলী

4

একাধিক এবং ভার্চুয়াল উত্তরাধিকার (গুলি) সম্পর্কে ইতিমধ্যে যা বলা হয়েছে তা ছাড়াও ড ডবসের জার্নালে একটি আকর্ষণীয় নিবন্ধ রয়েছে: একাধিক উত্তরাধিকার দরকারী হিসাবে বিবেচিত


1

আপনি কিছুটা বিভ্রান্ত হচ্ছেন আমি জানি না আপনি কিছু ধারণা মিশ্রিত করছেন কিনা।

আপনার ওপিতে আপনার ভার্চুয়াল বেস ক্লাস নেই। আপনার কেবল একটি বেস ক্লাস আছে।

আপনি ভার্চুয়াল উত্তরাধিকার করেছেন। এটি সাধারণত একাধিক উত্তরাধিকার হিসাবে ব্যবহৃত হয় যাতে একাধিক উত্স প্রাপ্ত শ্রেণিগুলি পুনর্গঠন ছাড়াই বেস শ্রেণির সদস্যদের ব্যবহার করে।

খাঁটি ভার্চুয়াল ফাংশন সহ একটি বেস ক্লাস তাত্ক্ষণিকভাবে করা হয় না। এর জন্য পল যে বাক্য গঠন করেছেন তা দরকার। এটি সাধারণত ব্যবহৃত হয় যাতে উদ্ভূত শ্রেণীদের অবশ্যই এই ফাংশনগুলি সংজ্ঞায়িত করতে হবে।

আমি এ সম্পর্কে আর কোনও ব্যাখ্যা দিতে চাই না কারণ আপনি যা চাইছেন তা আমি সম্পূর্ণরূপে পাই না।


1
ভার্চুয়াল উত্তরাধিকার হিসাবে ব্যবহৃত একটি "বেস ক্লাস" একটি "ভার্চুয়াল বেস ক্লাস" হয়ে যায় (সেই নির্দিষ্ট উত্তরাধিকারের প্রসঙ্গে)।
লুক হার্মিটে

1

এর অর্থ ভার্চুয়াল ফাংশনে একটি কল "ডান" শ্রেণিতে ফরোয়ার্ড করা হবে।

সি ++ এফএকিউ লাইট এফটিডাব্লু।

সংক্ষেপে, এটি প্রায়শই একাধিক-উত্তরাধিকারের পরিস্থিতিতে ব্যবহৃত হয়, যেখানে একটি "হীরা" শ্রেণিবদ্ধ গঠিত হয়। ভার্চুয়াল উত্তরাধিকার তখন নীচের শ্রেণিতে তৈরি অস্পষ্টতাকে ভেঙে ফেলবে, যখন আপনি class শ্রেণিতে ফাংশনটি কল করবেন এবং ফাংশনটি নীচের শ্রেণীর উপরে ডি 1 বা ডি 2 এর মধ্যে সমাধান করা দরকার। ডায়াগ্রাম এবং বিশদগুলির জন্য FAQ আইটেমটি দেখুন ।

এটি বোন প্রতিনিধিদলের ক্ষেত্রেও ব্যবহৃত হয় , এটি একটি শক্তিশালী বৈশিষ্ট্য (যদিও হৃদয়ের মূর্খতার জন্য নয়)। এই FAQ দেখুন ।

কার্যকর সি ++ তৃতীয় সংস্করণে আইটেম 40 দেখুন (দ্বিতীয় সংস্করণে 43)।


1

ডায়মন্ড উত্তরাধিকার চলমানযোগ্য ব্যবহারের উদাহরণ

এই উদাহরণটি দেখায় যে কীভাবে আদর্শ দৃশ্যে ভার্চুয়াল বেস শ্রেণিটি ব্যবহার করতে হয়: হীরার উত্তরাধিকার সমাধানের জন্য।

#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);
}

2
assert(A::aDefault == 0);মূল ফাংশন থেকে আমাকে একটি সংকলন ত্রুটি দেয়: aDefault is not a member of Aজিসিসি 5.4.0 ব্যবহার করে। মনে হয় কী করবেন?
SebNag

@ সেবতু আহ ধন্যবাদ, কপির পেস্ট থেকে মুছে ফেলার কিছু ভুলে গিয়েছিলাম, এখনই এটি মুছে ফেলুন। উদাহরণটি এটি ছাড়া এখনও অর্থবহ হওয়া উচিত।
সিরো সান্তিলি 郝海东 冠状 病 六四 事件

0

ভার্চুয়াল ক্লাস হয় না ভার্চুয়াল উত্তরাধিকারের মতো । ভার্চুয়াল ক্লাসগুলি আপনি ইনস্ট্যান্ট করতে পারবেন না, ভার্চুয়াল উত্তরাধিকার সম্পূর্ণ অন্য কিছু।

উইকিপিডিয়া এটির চেয়ে ভাল বর্ণনা করে। http://en.wikipedia.org/wiki/Virtual_inheritance


6
সি ++ তে "ভার্চুয়াল ক্লাস" বলে কোনও জিনিস নেই। তবে "ভার্চুয়াল বেস ক্লাস" রয়েছে যা প্রদত্ত উত্তরাধিকার সম্পর্কিত "ভার্চুয়াল"। আপনি যা উল্লেখ করেন তা হ'ল সরকারীভাবে "বিমূর্ত শ্রেণি" বলা হয়।
লুক হার্মিটে

@ লুকহর্মিট, সি ++ তে অবশ্যই ভার্চুয়াল ক্লাস রয়েছে। এটি দেখুন: en.wikedia.org/wiki/ ভার্চুয়াল_ক্লাস
রাফিদ

"ত্রুটি: 'ভার্চুয়াল' কেবলমাত্র ফাংশনের জন্য নির্দিষ্ট করা যেতে পারে"। আমি জানি না এটি কোন ভাষা। তবে সি ++ তে ভার্চুয়াল ক্লাসের মতো নির্দিষ্ট কিছু নেই ।
লুক হার্মিট

0

নিয়মিত উত্তরাধিকার

সাধারণ 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

এর পরে, এটি DerivedDerivedClassthe শ্রেণীর জন্য উপস্থাপনের 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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.