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




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

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

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

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

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

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

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

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

একটি বিশুদ্ধ ভার্চুয়াল ফাংশন সঙ্গে একটি বেস ক্লাস instantiated করা হয় না। এই পল পায় যে সিনট্যাক্স প্রয়োজন। এটি সাধারণত ব্যবহৃত হয় যাতে প্রাপ্ত শ্রেণিগুলি সেই ফাংশনগুলিকে সংজ্ঞায়িত করতে হবে।

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


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

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


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

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

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

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

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

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


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

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

আপনি এই বিষয়ে পড়ার পরে সত্যিই অন্য কোন বিষয় পড়তে হবে না (যদি না আপনি একটি কম্পাইলার লেখক হন) ...


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

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

  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%% আছে বলে জানিয়েছেন যে কোনও ক্রিয়াপদটি হ'ল।


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

উইকিপিডিয়া এটির চেয়ে ভাল বর্ণনা করে। 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