c++ - উনল - সি++, একটি ভার্চুয়াল বেস ক্লাস কি?




সি++ প্রোগ্রামিং বই ডাউনলোড (7)

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

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

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

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

একটি ভার্চুয়াল বেস ক্লাস এমন একটি শ্রেণী যা তাত্ক্ষণিকভাবে চালু করা যাবে না: আপনি এটির থেকে সরাসরি বস্তু তৈরি করতে পারবেন না।

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


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

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

  A
 / \
B   C
 \ /
  D

কিন্তু মেমরি বিন্যাসে আপনার আছে:

A   A
|   |
B   C
 \ /
  D

এটি ব্যাখ্যা করে কেন D::foo() কল করলে, আপনার একটি অস্পষ্টতা সমস্যা রয়েছে। কিন্তু আসল সমস্যা আসে যখন আপনি A সদস্য পরিবর্তনশীল ব্যবহার করতে চান। উদাহরণস্বরূপ, আসুন আমরা বলি:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

যখন আপনি D থেকে m_iValue অ্যাক্সেস করার চেষ্টা করবেন, কম্পাইলার প্রতিবাদ করবে, কারণ অনুক্রমের মধ্যে, এটি দুটি m_iValue দেখতে m_iValue , এক নয়। এবং যদি আপনি একটি সংশোধন করেন, বলুন, B::m_iValue (এটি হল A::m_iValue B পিতা), C::m_iValue সংশোধন করা হবে না (এটি হল C A::m_iValue C প্যারেন্ট)।

ভার্চুয়াল উত্তরাধিকারটি সহজেই আসে, এটির সাথে আপনি শুধুমাত্র একটি foo() পদ্ধতিতে নয়, কেবল একটি এবং m_iValue ছাড়াও সত্য হীরা m_iValue

কি ভুল হতে পারে?

কল্পনা করুন:

  • A কিছু মৌলিক বৈশিষ্ট্য আছে।
  • B এটিতে কিছু ধরণের শীতল অ্যারে যোগ করে (উদাহরণস্বরূপ)
  • C একটি পর্যবেক্ষক প্যাটার্ন মত কিছু শীতল বৈশিষ্ট্য যোগ করে (উদাহরণস্বরূপ, m_iValue উপর)।
  • B এবং C থেকে উত্তরাধিকার সূত্রে প্রাপ্ত, এবং এভাবে A

স্বাভাবিক উত্তরাধিকার সঙ্গে, D থেকে m_iValue পরিবর্তন করা দ্বিধান্বিত এবং এটি সমাধান করা আবশ্যক। এমনকি যদি এটির মধ্যেও দুটি m_iValues থাকে তবে আপনি এটি ভাল মনে রাখবেন এবং একই সময়ে দুটি আপডেট করুন।

ভার্চুয়াল উত্তরাধিকারের সাথে, D থেকে m_iValue পরিবর্তন করা ঠিক আছে ... কিন্তু ... বলুন আপনার কাছে D । তার C ইন্টারফেসের মাধ্যমে, আপনি একটি পর্যবেক্ষক সংযুক্ত। এবং তার B ইন্টারফেসের মাধ্যমে, আপনি শীতল অ্যারে আপডেট করুন, যা সরাসরি m_iValue পরিবর্তন করার পার্শ্ব প্রতিক্রিয়া আছে ...

m_iValue পরিবর্তনটি সরাসরি (কোনও ভার্চুয়াল অ্যাক্সেসার পদ্ধতি ব্যবহার না করে) করা হয়, পর্যবেক্ষক " C " এর মাধ্যমে "শোনাচ্ছে" বলা হবে না, কারণ শোনাচ্ছে এমন কোডটি C মধ্যে প্রয়োগ করা হয় এবং B এটি সম্পর্কে জানে না .. ।

উপসংহার

আপনি যদি আপনার আধিপত্যের মধ্যে হীরা ধারণ করেন তবে এর অর্থ হল আপনার কাছে 95%% আছে বলে জানিয়েছেন যে কোনও ক্রিয়াপদটি হ'ল।


আমি OJ এর ধরনের স্পষ্টতা যোগ করতে চাই।

ভার্চুয়াল উত্তরাধিকার একটি মূল্য ছাড়া আসে না। ভার্চুয়াল সব জিনিস সঙ্গে লাইক, আপনি একটি কর্মক্ষমতা আঘাত পেতে। এই কর্মক্ষমতা কাছাকাছি একটি উপায় সম্ভবত কম মার্জিত যে আঘাত আছে।

কার্যকরীভাবে হীরাটি ভেঙ্গে দেওয়ার পরিবর্তে, আপনি হীরাতে আরেকটি স্তর যোগ করতে পারেন, এটির মতো কিছু পেতে:

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

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

অবশ্যই, আপনি যদি D11 এবং D12 পরিবর্তন করতে পারেন তবে আপনি এই ক্লাসগুলির মধ্যে একই কৌশলটি করতে পারেন তবে প্রায়ই এটি এমন নয়।



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

নিম্নলিখিত দৃশ্যকল্প বিবেচনা করুন:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

উপরে বর্ণের অনুক্রমের ফলাফল "ভয়ঙ্কর হীরা" যা এই রকম দেখাচ্ছে:

  A
 / \
B   C
 \ /
  D

ডি-এর একটি উদাহরণ B এর তৈরি হবে, যার মধ্যে A, এবং C রয়েছে যা A কে অন্তর্ভুক্ত করে। সুতরাং আপনার দুটি "দৃষ্টান্ত" (একটি ভাল অভিব্যক্তির চাইতে) রয়েছে।

যখন আপনি এই দৃশ্যকল্প আছে, আপনি ambiguity সম্ভাবনা আছে। আপনি যখন এটি করবেন তখন কি হবে:

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

একটি সংক্ষিপ্ত সারাংশ হিসাবে সাহায্য করে যে আশা করি। আরো তথ্যের জন্য, this এবং this একটি পড়া আছে। একটি ভাল উদাহরণ here পাওয়া here


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

উইকিপিডিয়া এটির চেয়ে ভাল বর্ণনা করে। this


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

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

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




virtual-inheritance