c++ - tutorial - কন্সট্রাকটর ভিতরে ভার্চুয়াল ফাংশন কলিং



সি++ প্রোগ্রামিং বই (9)

ধরুন আমার দুটি সি ++ ক্লাস রয়েছে:

class A
{
public:
  A() { fn(); }

  virtual void fn() { _n = 1; }
  int getn() { return _n; }

protected:
  int _n;
};

class B : public A
{
public:
  B() : A() {}

  virtual void fn() { _n = 2; }
};

যদি আমি নিম্নলিখিত কোড লিখি:

int main()
{
  B b;
  int n = b.getn();
}

এক হতে পারে যে n সেট করা হয় 2।

এটা সক্রিয় করে যে n সেট করা হয় 1. কেন?

https://src-bin.com


Answer #1

Vtables কম্পাইলার দ্বারা তৈরি করা হয়। একটি বর্গ বস্তুর তার vtable একটি পয়েন্টার আছে। যখন এটি জীবন শুরু হয়, তখন সেই বুদ্ধিমান পয়েন্টার বেস বর্গের ভেলিয়েটে পয়েন্ট করে। কনস্ট্রাক্টর কোডের শেষে, কম্পাইলারটি ক্লাসের জন্য প্রকৃত ভেলিয়েটে vtable পয়েন্টারটিকে পুনরায় পয়েন্ট করতে কোড তৈরি করে। এটি নিশ্চিত করে যে কন্সট্রাকটর কোডটি ভার্চুয়াল ফাংশনগুলিকে কল করে সেই ফাংশনের বেস ক্লাস বাস্তবায়নগুলিকে কল করে ওভাররাইড নয়।


Answer #2

অন্য উত্তরগুলি ইতিমধ্যে ব্যাখ্যা করেছে কেন virtual ফাংশন কন্সট্রাকটর থেকে বলা হয় যখন প্রত্যাশিত হিসাবে কাজ করে না। আমি পরিবর্তে একটি বেস টাইপ এর কন্সট্রাকটর থেকে polymorphic মত আচরণ পাওয়ার জন্য অন্য সম্ভাব্য কাজ প্রস্তাব করা চাই।

বেস টাইপে টেম্পলেট কন্সট্রাকটর যুক্ত করে যেমন টেমপ্লেট যুক্তি সর্বদা উদ্ভূত প্রকারের জন্য কল্পনা করা হয়, এটি উদ্ভূত প্রকারের কংক্রিটের ধরন সম্পর্কে সচেতন হওয়া সম্ভব। সেখানে থেকে, আপনি যে derived ধরনের জন্য static সদস্য ফাংশন কল করতে পারেন।

এই সমাধান অ static সদস্য ফাংশন বলা যাবে না। নির্বাহটি বেস টাইপের কন্সট্রকটারে থাকে তবে, প্রাপ্ত প্রকারের কন্সট্রকটরটির সদস্যের সূচনা তালিকার মধ্য দিয়ে যেতে সময় নেই। তৈরি হওয়া ইনস্ট্যান্সের প্রাপ্ত প্রকার অংশটি এটি শুরু করা শুরু করা হয়নি। এবং যেহেতু অ static সদস্য ফাংশনগুলি প্রায়শই ডেটা সদস্যদের সাথে যোগাযোগ করে, তা বেস টাইপের কন্সট্রকটর থেকে উদ্ভূত ধরনগুলির অ static সদস্য ফাংশনগুলিতে কল করতে অস্বাভাবিক হবে।

এখানে একটি নমুনা বাস্তবায়ন:

#include <iostream>
#include <string>

struct Base {
protected:
    template<class T>
    explicit Base(const T*) : class_name(T::Name())
    {
        std::cout << class_name << " created\n";
    }

public:
    Base() : class_name(Name())
    {
        std::cout << class_name << " created\n";
    }


    virtual ~Base() {
        std::cout << class_name << " destroyed\n";
    }

    static std::string Name() {
        return "Base";
    }

private:
    std::string class_name;
};


struct Derived : public Base
{   
    Derived() : Base(this) {} // `this` is used to allow Base::Base<T> to deduce T

    static std::string Name() {
        return "Derived";
    }
};

int main(int argc, const char *argv[]) {

    Derived{};  // Create and destroy a Derived
    Base{};     // Create and destroy a Base

    return 0;
}

এই উদাহরণ মুদ্রণ করা উচিত

Derived created
Derived destroyed
Base created
Base destroyed

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


Answer #3

আপনি উইন্ডোজ এক্সপ্লোরার থেকে ক্র্যাশ ত্রুটি জানেন ?! "বিশুদ্ধ ভার্চুয়াল ফাংশন কল ..."
একই সমস্যা ...

class AbstractClass 
{
public:
    AbstractClass( ){
        //if you call pureVitualFunction I will crash...
    }
    virtual void pureVitualFunction() = 0;
};

কারন pureVitualFunction () এবং ফাংশনটির জন্য কোন আনুপাতিকতা নেই কারণ কনস্ট্রাক্টারে ফাংশনটি বলা হয়, প্রোগ্রামটি ক্র্যাশ করবে।


Answer #4

আমি এখানে ভার্চুয়াল কী শব্দ গুরুত্ব দেখছি না। b একটি স্ট্যাটিক-টাইপযুক্ত পরিবর্তনশীল এবং এটির কম্পাইলার কম্পাইলার দ্বারা নির্ধারিত হয়। ফাংশন কল vtable উল্লেখ করা হবে না। যখন b নির্মিত হয়, তার পিতা-মাতা ক্লাসের কনস্ট্রাক্টর বলা হয়, তাই _n এর মান 1 সেট করা হয়।


Answer #5

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

সি ++ প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী লাইট বেশ ভাল বিস্তারিত বিভাগ 23.7 এ এই জুড়ে। আমি একটি ফলোআপ জন্য যে (এবং FAQ এর বাকি) পড়া সুপারিশ।

উদ্ধৃতাংশ:

[...] একটি কন্সট্রাক্টারে, ভার্চুয়াল কল প্রক্রিয়া নিষ্ক্রিয় করা হয়েছে কারণ প্রাপ্ত হওয়া শ্রেণির থেকে মুছে ফেলা এখনো হয়নি। অবজেক্ট বেস বেস থেকে তৈরি করা হয়, "derived আগে বেস"।

[...]

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

সব সংশোধন সংশোধন করুন (ধন্যবাদ litb)


Answer #6

কারণ হল সি ++ বস্তুগুলি ভিতরের দিক থেকে পেঁয়াজের মত তৈরি করা হয়। সুপার ক্লাস derived ক্লাস আগে নির্মিত হয়। সুতরাং, একটি বি তৈরি করার আগে, একটি এ তৈরি করা আবশ্যক। যখন A এর কন্সট্রাকটর বলা হয়, এটি এখনও একটি বি নয়, তাই ভার্চুয়াল ফাংশন টেবিলে এখনও A এর fn () এর অনুলিপির জন্য এন্ট্রি রয়েছে।


Answer #7

বস্তুর কন্সট্রাকটর কল সময় ভার্চুয়াল ফাংশন পয়েন্টার টেবিল সম্পূর্ণরূপে নির্মিত হয় না। এটি করা আপনাকে সাধারণত আপনার প্রত্যাশিত আচরণ দেবে না। এই পরিস্থিতিতে একটি ভার্চুয়াল ফাংশন কল করা কাজ করতে পারে কিন্তু নিশ্চিত নয় এবং পোর্টেবল হওয়া এড়িয়ে চলতে হবে এবং C ++ মান অনুসরণ করুন।


Answer #8

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

যাইহোক, এটি পলিমারফিক গেটরগুলির সাথে সমাধান করা যেতে পারে যা স্ট্যাটিক পলিমারফিজম ব্যবহার করে, যদি আপনার গটার রিটার্ন স্টোনেন্টস বা অন্য কোনও স্ট্যাটিক সদস্য ফাংশনে প্রকাশ করা হয় তবে এই উদাহরণটি সিআরটিপি ( https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ব্যবহার করে। https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern )।

template<typename DerivedClass>
class Base
{
public:
    inline Base() :
    foo(DerivedClass::getFoo())
    {}

    inline int fooSq() {
        return foo * foo;
    }

    const int foo;
};

class A : public Base<A>
{
public:
    inline static int getFoo() { return 1; }
};

class B : public Base<B>
{
public:
    inline static int getFoo() { return 2; }
};

class C : public Base<C>
{
public:
    inline static int getFoo() { return 3; }
};

int main()
{
    A a;
    B b;
    C c;

    std::cout << a.fooSq() << ", " << b.fooSq() << ", " << c.fooSq() << std::endl;

    return 0;
}

স্ট্যাটিক পলিমার্ফিজম ব্যবহার করে, বেস ক্লাসটি জানে যে কোন শ্রেণীটি গেটরকে কল করার সময় তথ্যটি কম্পাইল-সময় সরবরাহ করা হয়।


Answer #9

সি ++ প্রশ্নাবলী লাইট এই চমত্কার ভাল আবরণ:

মূলত, বেস ক্লাস কনস্ট্রাক্টরকে কল করার সময়, বস্তুটি এখনও প্রাপ্ত হওয়া প্রকারের নয় এবং এইভাবে ভার্চুয়াল ফাংশনের বেস টাইপের বাস্তবায়ন বলা হয় এবং প্রাপ্ত হওয়া নয়।





virtual-method