ভার্চুয়াল ফাংশন এবং vtable কীভাবে প্রয়োগ করা হয়?


109

C ++ এ ভার্চুয়াল ফাংশনগুলি কী তা আমরা সকলেই জানি তবে কীভাবে এটি গভীর স্তরে বাস্তবায়ন করা হয়?

Vtable পরিবর্তন করা যেতে পারে বা এমনকি রানটাইমে সরাসরি অ্যাক্সেস করা যেতে পারে?

ভিটিবেল কি সমস্ত শ্রেণীর জন্য উপস্থিত রয়েছে, বা কেবলমাত্র যাদের কমপক্ষে একটি ভার্চুয়াল ফাংশন রয়েছে?

অ্যাবস্ট্রাক্ট ক্লাসে কমপক্ষে একটি প্রবেশের ফাংশন পয়েন্টারটির জন্য কেবল একটি নল থাকে?

একক ভার্চুয়াল ফাংশনটি কি পুরো ক্লাসকে ধীর করে দেয়? বা ভার্চুয়াল যে ফাংশন শুধুমাত্র কল? এবং যদি ভার্চুয়াল ফাংশনটি আসলে ওভাররাইট করা থাকে বা গতিটি প্রভাবিত হয়, বা ভার্চুয়াল হওয়ার কারণে এর কোনও প্রভাব নেই?


2
মাষ্টারপিস পড়া সুপারিশ Inside the C++ Object Modelদ্বারা Stanley B. Lippman। (বিভাগ 4.2, পৃষ্ঠা 124-131)
স্মোক উইকিপিডিয়া

উত্তর:


123

ভার্চুয়াল ফাংশন কীভাবে গভীর স্তরে প্রয়োগ করা হয়?

থেকে "C ++ ভার্চুয়াল কার্যাবলী" :

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

Vtable পরিবর্তন করা যেতে পারে বা এমনকি রানটাইমে সরাসরি অ্যাক্সেস করা যেতে পারে?

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

Vtable কি সমস্ত অবজেক্টের জন্য উপস্থিত রয়েছে, বা কেবলমাত্র তাদের মধ্যে কমপক্ষে একটি ভার্চুয়াল ফাংশন রয়েছে?

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

অ্যাবস্ট্রাক্ট ক্লাসে কমপক্ষে একটি প্রবেশের ফাংশন পয়েন্টারটির জন্য কেবল একটি নল থাকে?

উত্তরটি হ'ল এটি ভাষা স্পেস দ্বারা নির্ধারিত নয় সুতরাং এটি বাস্তবায়নের উপর নির্ভর করে। খাঁটি ভার্চুয়াল ফাংশন কল করার পরে এটি সংজ্ঞায়িত না হলে (যা এটি সাধারণত হয় না) অনির্ধারিত আচরণের ফলাফল দেয় (আইএসও / আইইসি 14882: 2003 10.4-2)। অনুশীলনে এটি ফাংশনের জন্য ভিটিবেলে একটি স্লট বরাদ্দ করে তবে এটিতে কোনও ঠিকানা বরাদ্দ করে না। এটি ভেটেবলকে অসম্পূর্ণ রাখে যার ফলে ডেরিভড ক্লাসগুলি ফাংশনটি বাস্তবায়িত করতে এবং ভেটেবলটি সম্পূর্ণ করতে হবে। কিছু বাস্তবায়ন কেবল ভিটিবেল এন্ট্রিতে একটি নল পয়েন্টার রাখে; অন্যান্য প্রয়োগগুলি একটি ডামি পদ্ধতিতে একটি পয়েন্টার রাখে যা দৃ an়তার অনুরূপ কিছু করে।

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

একটি ভার্চুয়াল ফাংশন থাকার ফলে কি পুরো ক্লাসটি ধীর হয়ে যায় বা ভার্চুয়াল যে ফাংশনে কেবলমাত্র কল আসে?

এটি আমার জ্ঞানের প্রান্তে পৌঁছে যাচ্ছে, সুতরাং কেউ যদি ভুল হয় তবে আমাকে এখানে সাহায্য করুন!

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

ভার্চুয়াল ফাংশনটি আসলে ওভাররাইড করা বা না হলে গতিটি কী প্রভাবিত হয়, বা ভার্চুয়াল হওয়ার সাথে এতক্ষণ এর কোনও প্রভাব নেই?

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

অতিরিক্ত সম্পদ:

http://www.codersource.net/published/view/325/virtual_functions_in.aspx (মাধ্যমে পথ ফিরে মেশিন)
http://en.wikipedia.org/wiki/Virtual_table
http://www.codesourcery.com/public/ cxx আবি / abi.html # vtable


2
কোনও কম্পাইলারের জন্য কোনও বস্তুর মধ্যে অপ্রয়োজনীয় vtable পয়েন্টার রাখা দরকার যা এটির প্রয়োজন হয় না এটি স্ট্রস্ট্রুপের সি ++ এর দর্শনের সাথে সঙ্গতিপূর্ণ নয়। নিয়মটি হ'ল আপনি ওভারহেড পাবেন না যা সি এর মধ্যে না থাকলে আপনি জিজ্ঞাসা না করেন এবং এটির বিরতি রচনাকারীদের পক্ষে এটি অভদ্র।
স্টিভ জেসোপ 19:58

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

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

1
আমি বিশ্বাস করি যখন একটি ক্লাসে কমপক্ষে একটি ভার্চুয়াল ফাংশন থাকে, প্রতিটি বস্তুর একটি vtable থাকে এবং পুরো বর্গের জন্য একটি থাকে না।
আসফ আর

3
সাধারণ বাস্তবায়ন: প্রতিটি বস্তুর একটি ভেটেবলের পয়েন্টার থাকে; ক্লাস টেবিলের মালিক। ভিত্তি কর্টরটি শেষ হওয়ার পরে, নির্মাণ যাদুটিতে ডাইর্টেড কর্টারে ভিটিবল পয়েন্টারটি আপডেট করা সহজভাবে গঠিত।
এমএসএলটার

31
  • Vtable পরিবর্তন করা যেতে পারে বা এমনকি রানটাইমে সরাসরি অ্যাক্সেস করা যেতে পারে?

বাহ্যিকভাবে নয়, তবে আপনি যদি নোংরা কৌশলগুলিতে আপত্তি না জানান, নিশ্চিত!

সতর্কতা : এই কৌশলটি শিশু, 969 বছরের কম বয়সী বা আলফা সেন্টাউরির ছোট ছোট লোমযুক্ত প্রাণী দ্বারা ব্যবহারের জন্য প্রস্তাবিত নয় । পার্শ্ব প্রতিক্রিয়াগুলির মধ্যে এমন নমনীয়দের অন্তর্ভুক্ত থাকতে পারে যা আপনার নাক থেকে বেরিয়ে আসে , পরবর্তী সমস্ত কোড পর্যালোচনাগুলিতে প্রয়োজনীয় অনুমোদক হিসাবে যোগ-সোথোথের আকস্মিক উপস্থিতি , বা IHuman::PlayPiano()সমস্ত বিদ্যমান দৃষ্টান্তে প্রত্যাবর্তনমূলক যোগ ]

বেশিরভাগ সংকলকগুলিতে আমি দেখেছি, ভিটিবিএল * হ'ল অবজেক্টের প্রথম 4 বাইট, এবং ভিটিবিএল বিষয়বস্তু কেবল সেখানে সদস্য পয়েন্টারগুলির একটি অ্যারে (সাধারণত তারা ক্রম হিসাবে ঘোষণা করা হয়েছিল, বেস শ্রেণীর প্রথম সহ)। অন্যান্য সম্ভাব্য লেআউট অবশ্যই রয়েছে, তবে এটি আমি সাধারণত লক্ষ্য করেছি।

class A {
  public:
  virtual int f1() = 0;
};
class B : public A {
  public:
  virtual int f1() { return 1; }
  virtual int f2() { return 2; }
};
class C : public A {
  public:
  virtual int f1() { return -1; }
  virtual int f2() { return -2; }
};

A *x = new B;
A *y = new C;
A *z = new C;

এখন কিছু শেনানিগান টানতে ...

রানটাইমে ক্লাস পরিবর্তন করা:

std::swap(*(void **)x, *(void **)y);
// Now x is a C, and y is a B! Hope they used the same layout of members!

সমস্ত দৃষ্টান্তের জন্য একটি পদ্ধতি প্রতিস্থাপন (একটি শ্রেণি বানর পাঠাচ্ছে)

এটি একটি সামান্য কৌশলযুক্ত, যেহেতু ভিটিবিএল নিজেই কেবল পঠনযোগ্য মেমরিতে রয়েছে।

int f3(A*) { return 0; }

mprotect(*(void **)x,8,PROT_READ|PROT_WRITE|PROT_EXEC);
// Or VirtualProtect on win32; this part's very OS-specific
(*(int (***)(A *)x)[0] = f3;
// Now C::f1() returns 0 (remember we made x into a C above)
// so x->f1() and z->f1() both return 0

পরেরটি ভাইরাস-চেকারগুলি তৈরি করতে এবং লিঙ্কটি জাগ্রত করে এবং বিজ্ঞপ্তি নেবে, এমপ্রোটেক্ট হেরফেরের কারণে। NX বিট ব্যবহার করার প্রক্রিয়াতে এটি ভাল ব্যর্থ হতে পারে।


6
হুম। এটি অশুভ লাগে যে এটি একটি অনুগ্রহ পেয়েছে। আমি আশা করি এর অর্থ এই নয় যে মোবাইলবাইটস মনে করে যে এই ধরনের শেনানিগানরা আসলে একটি ভাল ধারণা ...
পিত্জক

1
দয়া করে এই কৌশলটি ব্যবহারকে নিখুঁতভাবে বিবেচনা করুন, "উইঙ্কিং" না করে পরিষ্কার এবং দৃ strongly়তার সাথে।
einpoklum

" ভিটিবিএল বিষয়বস্তুগুলি কেবল সদস্য পয়েন্টারগুলির একটি অ্যারে " আসলে এটি বিভিন্ন রেকর্ডগুলির সাথে একটি রেকর্ড (একটি কাঠামো), এটি সমানভাবে ব্যবধানে ঘটে
কৌতূহলী

1
আপনি যেভাবে এটি তাকান করতে পারেন; ফাংশন পয়েন্টারগুলির বিভিন্ন স্বাক্ষর রয়েছে, এবং এইভাবে বিভিন্ন পয়েন্টার ধরণের রয়েছে; এই অর্থে, এটি প্রকৃতপক্ষে কাঠামোর মতো। তবে অন্যান্য প্রসঙ্গে, তবে ভিটিবিএল সূচকের ধারণাটি দরকারী (উদাহরণস্বরূপ অ্যাক্টিভেক্স এটি টাইপলিবসে দ্বৈত ইন্টারফেস বর্ণনা করার পদ্ধতিতে ব্যবহার করে), যা আরও অ্যারে-জাতীয় মত like
পিত্তজক

17

একক ভার্চুয়াল ফাংশনটি কি পুরো ক্লাসকে ধীর করে দেয়?

বা ভার্চুয়াল যে ফাংশন শুধুমাত্র কল? এবং যদি ভার্চুয়াল ফাংশনটি আসলে ওভাররাইট করা থাকে বা গতিটি প্রভাবিত হয়, বা ভার্চুয়াল হওয়ার কারণে এর কোনও প্রভাব নেই?

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

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

struct Foo { virtual ~Foo(); virtual int a() { return 1; } };
struct Bar: public Foo { int a() { return 2; } };
void f(Foo& arg) {
  Foo x; x.a(); // non-virtual: always calls Foo::a()
  Bar y; y.a(); // non-virtual: always calls Bar::a()
  arg.a();      // virtual: must dispatch via vtable
  Foo z = arg;  // copy constructor Foo::Foo(const Foo&) will convert to Foo
  z.a();        // non-virtual Foo::a, since z is a Foo, even if arg was not
}

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

এগুলি কীভাবে গভীর স্তরে বাস্তবায়ন করা হয়?

আমি একটি মোক প্রয়োগ বাস্তবায়ন ব্যবহার করে এটি প্রদর্শনের জন্য ঝেরিকোর ধারণাটি পছন্দ করি। তবে আমি উপরের কোডের অনুরূপ কিছু বাস্তবায়নের জন্য সি ব্যবহার করব, যাতে নিম্ন স্তরটি আরও সহজে দেখা যায়।

প্যারেন্ট ক্লাস ফু

typedef struct Foo_t Foo;   // forward declaration
struct slotsFoo {           // list all virtual functions of Foo
  const void *parentVtable; // (single) inheritance
  void (*destructor)(Foo*); // virtual destructor Foo::~Foo
  int (*a)(Foo*);           // virtual function Foo::a
};
struct Foo_t {                      // class Foo
  const struct slotsFoo* vtable;    // each instance points to vtable
};
void destructFoo(Foo* self) { }     // Foo::~Foo
int aFoo(Foo* self) { return 1; }   // Foo::a()
const struct slotsFoo vtableFoo = { // only one constant table
  0,                                // no parent class
  destructFoo,
  aFoo
};
void constructFoo(Foo* self) {      // Foo::Foo()
  self->vtable = &vtableFoo;        // object points to class vtable
}
void copyConstructFoo(Foo* self,
                      Foo* other) { // Foo::Foo(const Foo&)
  self->vtable = &vtableFoo;        // don't copy from other!
}

উত্পন্ন ক্লাস বার

typedef struct Bar_t {              // class Bar
  Foo base;                         // inherit all members of Foo
} Bar;
void destructBar(Bar* self) { }     // Bar::~Bar
int aBar(Bar* self) { return 2; }   // Bar::a()
const struct slotsFoo vtableBar = { // one more constant table
  &vtableFoo,                       // can dynamic_cast to Foo
  (void(*)(Foo*)) destructBar,      // must cast type to avoid errors
  (int(*)(Foo*)) aBar
};
void constructBar(Bar* self) {      // Bar::Bar()
  self->base.vtable = &vtableBar;   // point to Bar vtable
}

ফাংশন চ ভার্চুয়াল ফাংশন কল সম্পাদন

void f(Foo* arg) {                  // same functionality as above
  Foo x; constructFoo(&x); aFoo(&x);
  Bar y; constructBar(&y); aBar(&y);
  arg->vtable->a(arg);              // virtual function call
  Foo z; copyConstructFoo(&z, arg);
  aFoo(&z);
  destructFoo(&z);
  destructBar(&y);
  destructFoo(&x);
}

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

যদি argটাইপ হয় Foo*এবং আপনি গ্রহণ করেন arg->vtableতবে বাস্তবে এটি কোনও ধরণের জিনিস হয় Barতবে আপনি এখনও সঠিক ঠিকানাটি পান vtable। কারণ এটি vtableহ'ল বস্তুর ঠিকানায় সর্বদা প্রথম উপাদান হ'ল, এটি ডাকা হোক vtableবা base.vtableসঠিকভাবে টাইপ করা এক্সপ্রেশন হোক।


"পলিমারফিক ক্লাসের প্রতিটি বস্তু তার নিজস্ব vtable এ নির্দেশ করবে point" আপনি কি বলছেন যে প্রতিটি বস্তুর নিজস্ব vtable আছে? আফাইক ভিটিবেল একই শ্রেণীর সমস্ত বস্তুর মধ্যে ভাগ করা হয়। আমি ভুল হলে আমাকে জানাবেন।
ভুয়ান

1
@ ভুওয়ান: না, আপনি ঠিক বলেছেন: প্রকারভেদে কেবলমাত্র একটি ভ্যাটেবল রয়েছে (যা টেমপ্লেটের ক্ষেত্রে টেম্পলেট ইনস্ট্যান্টেশন প্রতি হতে পারে)। আমি বলতে চাইছিলাম একটি বহুবর্ষীক শ্রেণীর প্রতিটি বস্তু যার সাথে প্রযোজ্য ভেটেবলের প্রতি বিন্দু রয়েছে, সুতরাং প্রতিটি বস্তুর এই জাতীয় পয়েন্টার রয়েছে তবে একই ধরণের বস্তুর জন্য এটি একই টেবিলের দিকে নির্দেশ করবে। সম্ভবত আমার এই শব্দটি করা উচিত।
এমভিজি

1
@ এমভিজি " একই ধরণের অবজেক্টগুলি এটি একই টেবিলের দিকে নির্দেশ করবে " ভার্চুয়াল বেস ক্লাস সহ বেস ক্লাসগুলি নির্মাণের সময় নয়! (একটি খুব বিশেষ কেস)
কৌতূহলী

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


2

এই উত্তরটি সম্প্রদায় উইকির উত্তরে অন্তর্ভুক্ত করা হয়েছে

  • অ্যাবস্ট্রাক্ট ক্লাসে কমপক্ষে একটি প্রবেশের ফাংশন পয়েন্টারটির জন্য কেবল একটি নল থাকে?

এর উত্তরটি হ'ল এটি অনির্ধারিত - খাঁটি ভার্চুয়াল ফাংশনকে কল করা যদি সংজ্ঞায়িত না হয় (যা এটি সাধারণত হয় না) অপরিজ্ঞাত আচরণের ফলাফল দেয় (আইএসও / আইসিআই 14882: 2003 10.4-2)। কিছু বাস্তবায়ন কেবল ভিটিবেল এন্ট্রিতে একটি নল পয়েন্টার রাখে; অন্যান্য প্রয়োগগুলি একটি ডামি পদ্ধতিতে একটি পয়েন্টার রাখে যা দৃ an়তার অনুরূপ কিছু করে।

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


এছাড়াও, আমি মনে করি না যে একটি বিমূর্ত শ্রেণি একটি খাঁটি ভার্চুয়াল ফাংশনটির জন্য একটি বাস্তবায়ন সংজ্ঞায়িত করতে পারে। সংজ্ঞা দিয়ে, একটি খাঁটি ভার্চুয়াল ফাংশনটির কোনও দেহ নেই (যেমন বুল মাই_ফঙ্ক () = 0;)। তবে আপনি নিয়মিত ভার্চুয়াল ফাংশনগুলির জন্য বাস্তবায়ন সরবরাহ করতে পারেন।
জাচ বার্লিংগেম

খাঁটি ভার্চুয়াল ফাংশনটির একটি সংজ্ঞা থাকতে পারে। স্কট মায়ার্সের "কার্যকর সি ++, তৃতীয় এড" আইটেম # 34, আইএসও 14882-2003 10.4-2, বা বাইটস
মাইকেল

2

আপনি C ++ এ ভার্চুয়াল ফাংশনগুলির কার্যকারিতাটি পুনরায় তৈরি করতে পারেন ক্রম পয়েন্টার ব্যবহার করে একটি ক্লাসের সদস্য হিসাবে এবং স্থিতিশীল ফাংশনগুলির প্রয়োগ হিসাবে, বা পয়েন্টার ব্যবহার করে সদস্য ফাংশন এবং সদস্য ফাংশনগুলি প্রয়োগের জন্য। দুটি পদ্ধতির মধ্যে কেবলমাত্র বৈজ্ঞানিক সুবিধাগুলি রয়েছে ... আসলে ভার্চুয়াল ফাংশন কলগুলি কেবল একটি স্বীকৃত সুবিধা। প্রকৃতপক্ষে উত্তরাধিকার হ'ল একটি নীতিগত সুবিধার্থে ... উত্তরাধিকারের জন্য ভাষার বৈশিষ্ট্যগুলি ব্যবহার না করে এগুলি সমস্ত প্রয়োগ করা যেতে পারে। :)

নীচেটি ক্রেপ অচিহ্নিত, সম্ভবত বগী কোড, তবে আশা করি ধারণাটি প্রদর্শন করে।

যেমন

class Foo
{
protected:
 void(*)(Foo*) MyFunc;
public:
 Foo() { MyFunc = 0; }
 void ReplciatedVirtualFunctionCall()
 {
  MyFunc(*this);
 }
...
};

class Bar : public Foo
{
private:
 static void impl1(Foo* f)
 {
  ...
 }
public:
 Bar() { MyFunc = impl1; }
...
};

class Baz : public Foo
{
private:
 static void impl2(Foo* f)
 {
  ...
 }
public:
 Baz() { MyFunc = impl2; }
...
};

void(*)(Foo*) MyFunc;এটি কিছু জাভা বাক্য গঠন?
কৌতূহলী

না, ফাংশন পয়েন্টারগুলির জন্য এর সি / সি ++ সিনট্যাক্স। নিজেকে উদ্ধৃত করতে "আপনি ফাংশন পয়েন্টার ব্যবহার করে সি ++ তে ভার্চুয়াল ফাংশনগুলির কার্যকারিতা পুনরায় তৈরি করতে পারেন"। এটি সিনট্যাক্সের একটি কদর্য বিট, তবে আপনি যদি নিজেকে একজন সি প্রোগ্রামার হিসাবে বিবেচনা করেন তবে তার সাথে পরিচিত হওয়ার মতো কিছু।
riেরিকো

ac ফাংশন পয়েন্টারটি আরও দেখতে পাবেন: int ( PROC) (); এবং ক্লাস সদস্য ফাংশনটির পয়েন্টারটি দেখতে পাবেন: int (ClassName :: MPROC) ();
মেনেইস

1
@ অন্যদিকে, আপনি সেখানে কিছু সিনট্যাক্স ভুলে গেছেন ... আপনি টাইপিডের কথা ভাবছেন? টাইপডেফ ইন্ট (* পিআরসি) (); সুতরাং আপনি ইন্ট (* foo) () এর পরিবর্তে পরে কেবল প্রোসি ফু করতে পারেন?
ঝেরিকো

2

আমি এটিকে সহজ করার চেষ্টা করব :)

C ++ এ ভার্চুয়াল ফাংশনগুলি কী তা আমরা সকলেই জানি তবে কীভাবে এটি গভীর স্তরে বাস্তবায়ন করা হয়?

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

যখন পলিমারফিক ক্লাস অন্য পলিমারফিক ক্লাস থেকে উদ্ভূত হয় তখন আমাদের নিম্নলিখিত পরিস্থিতিতে থাকতে পারে:

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

Vtable পরিবর্তন করা যেতে পারে বা এমনকি রানটাইমে সরাসরি অ্যাক্সেস করা যেতে পারে?

স্ট্যান্ডার্ড উপায় নয় - এগুলি অ্যাক্সেসের জন্য কোনও এপিআই নেই। সংযোগকারীদের এগুলি অ্যাক্সেস করার জন্য কিছু এক্সটেনশন বা প্রাইভেট এপিআই থাকতে পারে তবে এটি কেবলমাত্র একটি এক্সটেনশন হতে পারে।

ভিটিবেল কি সমস্ত শ্রেণীর জন্য উপস্থিত রয়েছে, বা কেবলমাত্র যাদের কমপক্ষে একটি ভার্চুয়াল ফাংশন রয়েছে?

কেবলমাত্র যাদের কমপক্ষে একটি ভার্চুয়াল ফাংশন রয়েছে (এটি এমনকি ডেস্ট্রাক্টরও হোক) বা কমপক্ষে একটি শ্রেণি তৈরি করুন যার এর ভিটিবেল রয়েছে ("এটি পলিমর্ফিক")।

অ্যাবস্ট্রাক্ট ক্লাসে কমপক্ষে একটি প্রবেশের ফাংশন পয়েন্টারটির জন্য কেবল একটি নল থাকে?

এটি একটি সম্ভাব্য বাস্তবায়ন, বরং অনুশীলন নয়। পরিবর্তে একটি ফাংশন থাকে যা "খাঁটি ভার্চুয়াল ফাংশন বলে" এর মতো কিছু মুদ্রণ করে abort()। আপনি যদি কনস্ট্রাক্টর বা ডেস্ট্রাক্টরের বিমূর্ত পদ্ধতিতে কল করার চেষ্টা করেন তবে সেই কল হতে পারে।

একক ভার্চুয়াল ফাংশনটি কি পুরো ক্লাসকে ধীর করে দেয়? বা ভার্চুয়াল যে ফাংশন শুধুমাত্র কল? এবং যদি ভার্চুয়াল ফাংশনটি আসলে ওভাররাইট করা থাকে বা গতিটি প্রভাবিত হয়, বা ভার্চুয়াল হওয়ার কারণে এর কোনও প্রভাব নেই?

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

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

  • আপনি যদি কোনও মানটির মাধ্যমে পদ্ধতিটি কল করেন (কোনও মান পরিবর্তন করে এমন একটি ফাংশনের পরিবর্তনশীল বা ফলাফল) - এই ক্ষেত্রে সংকলকটির অবজেক্টের আসল শ্রেণিটি কোন সন্দেহ নেই এবং সংকলনের সময় এটি "কঠোর-সমাধান" করতে পারে ।
  • যদি ভার্চুয়াল পদ্ধতিটি finalক্লাসে ঘোষিত হয় যেখানে আপনি পয়েন্টার বা রেফারেন্স পেয়েছেন যার মাধ্যমে আপনি এটি কল করেছেন ( কেবলমাত্র C ++ 11 এ )। এই ক্ষেত্রে সংকলক জানে যে এই পদ্ধতিটি আর কোনও ওভাররাইডিং করতে পারে না এবং এটি কেবল এই শ্রেণীর থেকে পদ্ধতি হতে পারে।

উল্লেখ্য যে ভার্চুয়াল কলগুলির মধ্যে দুটি পয়েন্টারকে কেবলমাত্র ডিফারেন্সিংয়ের ওভারহেড রয়েছে। আরটিটিআই ব্যবহার করা (যদিও এটি কেবল পলিমারফিক ক্লাসের জন্য উপলভ্য) ভার্চুয়াল পদ্ধতিগুলি কল করার চেয়ে ধীরে ধীরে, আপনার কি একই জিনিসটিকে এই জাতীয় দুটি উপায়ে বাস্তবায়নের জন্য কোনও মামলা পাওয়া উচিত? উদাহরণস্বরূপ, সংজ্ঞা দেওয়া virtual bool HasHoof() { return false; }এবং তারপরে কেবল ওভাররাইড করা যেমন bool Horse::HasHoof() { return true; }আপনাকে কল করার ক্ষমতা if (anim->HasHoof())দেয় যা চেষ্টা করার চেয়ে দ্রুত হবে if(dynamic_cast<Horse*>(anim))। এর কারণ হ'ল dynamic_castপ্রকৃত পয়েন্টার টাইপ এবং কাঙ্ক্ষিত শ্রেণীর ধরণ থেকে কোনও পথ তৈরি করা যায় কিনা তা এমনকি পুনরাবৃত্তভাবে কিছু ক্ষেত্রে ক্লাসের শ্রেণিবিন্যাসের মধ্য দিয়ে যেতে হয়েছিল। ভার্চুয়াল কল সর্বদা একরকম থাকে - দুটি পয়েন্টারকে ডিফারেন্স করে।


2

এখানে আধুনিক সি ++ এ ভার্চুয়াল টেবিলের একটি চলমান ম্যানুয়াল বাস্তবায়ন। এটিতে সঠিকভাবে সংজ্ঞায়িত শব্দার্থক শব্দ রয়েছে, কোনও হ্যাক এবং নেই void*

দ্রষ্টব্য: .*এবং ->*তুলনায় ভিন্ন অপারেটার হয় *এবং ->। সদস্য ফাংশন পয়েন্টারগুলি ভিন্নভাবে কাজ করে।

#include <iostream>
#include <vector>
#include <memory>

struct vtable; // forward declare, we need just name

class animal
{
public:
    const std::string& get_name() const { return name; }

    // these will be abstract
    bool has_tail() const;
    bool has_wings() const;
    void sound() const;

protected: // we do not want animals to be created directly
    animal(const vtable* vtable_ptr, std::string name)
    : vtable_ptr(vtable_ptr), name(std::move(name)) { }

private:
    friend vtable; // just in case for non-public methods

    const vtable* const vtable_ptr;
    std::string name;
};

class cat : public animal
{
public:
    cat(std::string name);

    // functions to bind dynamically
    bool has_tail() const { return true; }
    bool has_wings() const { return false; }
    void sound() const
    {
        std::cout << get_name() << " does meow\n"; 
    }
};

class dog : public animal
{
public:
    dog(std::string name);

    // functions to bind dynamically
    bool has_tail() const { return true; }
    bool has_wings() const { return false; }
    void sound() const
    {
        std::cout << get_name() << " does whoof\n"; 
    }
};

class parrot : public animal
{
public:
    parrot(std::string name);

    // functions to bind dynamically
    bool has_tail() const { return false; }
    bool has_wings() const { return true; }
    void sound() const
    {
        std::cout << get_name() << " does crrra\n"; 
    }
};

// now the magic - pointers to member functions!
struct vtable
{
    bool (animal::* const has_tail)() const;
    bool (animal::* const has_wings)() const;
    void (animal::* const sound)() const;

    // constructor
    vtable (
        bool (animal::* const has_tail)() const,
        bool (animal::* const has_wings)() const,
        void (animal::* const sound)() const
    ) : has_tail(has_tail), has_wings(has_wings), sound(sound) { }
};

// global vtable objects
const vtable vtable_cat(
    static_cast<bool (animal::*)() const>(&cat::has_tail),
    static_cast<bool (animal::*)() const>(&cat::has_wings),
    static_cast<void (animal::*)() const>(&cat::sound));
const vtable vtable_dog(
    static_cast<bool (animal::*)() const>(&dog::has_tail),
    static_cast<bool (animal::*)() const>(&dog::has_wings),
    static_cast<void (animal::*)() const>(&dog::sound));
const vtable vtable_parrot(
    static_cast<bool (animal::*)() const>(&parrot::has_tail),
    static_cast<bool (animal::*)() const>(&parrot::has_wings),
    static_cast<void (animal::*)() const>(&parrot::sound));

// set vtable pointers in constructors
cat::cat(std::string name) : animal(&vtable_cat, std::move(name)) { }
dog::dog(std::string name) : animal(&vtable_dog, std::move(name)) { }
parrot::parrot(std::string name) : animal(&vtable_parrot, std::move(name)) { }

// implement dynamic dispatch
bool animal::has_tail() const
{
    return (this->*(vtable_ptr->has_tail))();
}

bool animal::has_wings() const
{
    return (this->*(vtable_ptr->has_wings))();
}

void animal::sound() const
{
    (this->*(vtable_ptr->sound))();
}

int main()
{
    std::vector<std::unique_ptr<animal>> animals;
    animals.push_back(std::make_unique<cat>("grumpy"));
    animals.push_back(std::make_unique<cat>("nyan"));
    animals.push_back(std::make_unique<dog>("doge"));
    animals.push_back(std::make_unique<parrot>("party"));

    for (const auto& a : animals)
        a->sound();

    // note: destructors are not dispatched virtually
}

1

প্রতিটি বস্তুর একটি vtable পয়েন্টার রয়েছে যা সদস্য ফাংশনের একটি অ্যারেতে নির্দেশ করে।


1

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


0

বার্লির উত্তরগুলি এখানে প্রশ্ন ব্যতীত সঠিক:

অ্যাবস্ট্রাক্ট ক্লাসে কমপক্ষে একটি প্রবেশের ফাংশন পয়েন্টারটির জন্য কেবল একটি নল থাকে?

উত্তরটি হ'ল বিমূর্ত শ্রেণীর জন্য কোনও ভার্চুয়াল টেবিল তৈরি করা হয়নি। এই ক্লাসগুলির কোনও অবজেক্ট তৈরি করা যায় না বলে কোনও প্রয়োজন নেই!

অন্য কথায় যদি আমাদের থাকে:

class B { ~B() = 0; }; // Abstract Base class
class D : public B { ~D() {} }; // Concrete Derived class

D* pD = new D();
B* pB = pD;

পিবির মাধ্যমে অ্যাক্সেস করা ভিটিবিএল পয়েন্টারটি ডি ডি ক্লাসের ভিটিবিএল হবে ঠিক এভাবেই বহুবর্ষ বাস্তবায়ন করা হয়। অর্থাত্, পিবির মাধ্যমে কীভাবে ডি পদ্ধতি ব্যবহার করা হয়। বি ক্লাসের জন্য কোনও ভিটিবিএলের দরকার নেই

মাইকের নীচে দেওয়া মন্তব্যের জবাবে ...

আমার বর্ণনায় বি শ্রেণীর যদি ভার্চুয়াল পদ্ধতি foo () থাকে যা ডি দ্বারা ওভাররাইড হয় না এবং ভার্চুয়াল পদ্ধতি বার () ওভাররাইড হয় তবে ডি এর ভিটিবিএল এর বি এর foo () এবং তার নিজস্ব বারে () নির্দেশক থাকবে । বি এর জন্য এখনও কোনও ভিটিবিএল তৈরি করা হয়নি


এটি 2 কারণে সঠিক নয়: 1) একটি বিমূর্ত শ্রেণীর খাঁটি ভার্চুয়াল পদ্ধতি ছাড়াও নিয়মিত ভার্চুয়াল পদ্ধতি থাকতে পারে এবং 2) খাঁটি ভার্চুয়াল পদ্ধতিতে optionচ্ছিকভাবে একটি সংজ্ঞা থাকতে পারে যা পুরোপুরি যোগ্যতাসম্পন্ন নামের সাথে ডাকা যেতে পারে।
মাইকেল বুড়

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

1
" উত্তরটি হ'ল বিমূর্ত শ্রেণীর জন্য কোনও ভার্চুয়াল টেবিল তৈরি করা হয় না " "ভুল। " এই ক্লাসগুলির কোনও বস্তু তৈরি করা যায় না বলে কোনও প্রয়োজন নেই! " ভুল ong
কৌতূহলী

আমি তোমার যুক্তিপূর্ণ কোনো vtable জন্য অনুসরণ করতে পারেন B উচিত প্রয়োজন হতে। কেবলমাত্র এর কয়েকটি পদ্ধতির (ডিফল্ট) বাস্তবায়ন হওয়ার অর্থ এই নয় যে সেগুলি একটি ভ্যাটেবলে সংরক্ষণ করতে হবে। তবে আমি কেবল আপনার কোডটি চালিয়েছি (এটি সংকলন করার জন্য কিছু সংশোধন করা হয়েছে) এর gcc -Sপরে c++filtএবং সেখানে স্পষ্টভাবে Bঅন্তর্ভুক্ত করার জন্য একটি ভেটেবল রয়েছে। আমার ধারণা এটি হতে পারে কারণ ভিটিবেল ক্লাসের নাম এবং উত্তরাধিকারের মতো আরটিটিআই ডেটাও সঞ্চয় করে। এটি একটি প্রয়োজন হতে পারে dynamic_cast<B*>। এমনকি -fno-rttiভিটিবেলকে দূরে সরিয়ে দেয় না। সঙ্গে clang -O3পরিবর্তে gccএটা হঠাৎ চলে গেছে।
এমভিজি

@ এমভিজি " কেবলমাত্র এর কয়েকটি পদ্ধতির (ডিফল্ট) বাস্তবায়ন হওয়ার অর্থ এই নয় যে সেগুলি একটি ভিটিবেলে সংরক্ষণ করতে হবে " হ্যাঁ, এর অর্থ কেবল এটি।
কৌতূহলী

0

ধারণার খুব সুন্দর প্রমাণ আমি কিছুটা আগে তৈরি করেছি (উত্তরাধিকারের বিষয়ে ক্রমগুলি অর্ডার দেওয়ার জন্য দেখুন); আপনার সি ++ এর বাস্তবায়ন আসলে এটি প্রত্যাখ্যান করে কিনা তা আমাকে জানতে দিন (আমার সিসির সংস্করণটি বেনামে স্ট্রাক্ট দেওয়ার জন্য কেবল একটি সতর্কতা দেয়, তবে এটি একটি বাগ), আমি আগ্রহী।

সিসিপোলাইট এইচ :

#ifndef CCPOLITE_H
#define CCPOLITE_H

/* the vtable or interface */
typedef struct {
    void (*Greet)(void *);
    void (*Thank)(void *);
} ICCPolite;

/**
 * the actual "object" literal as C++ sees it; public variables be here too 
 * all CPolite objects use(are instances of) this struct's structure.
 */
typedef struct {
    ICCPolite *vtbl;
} CPolite;

#endif /* CCPOLITE_H */

CCPolite_constructor.h :

/** 
 * unconventionally include me after defining OBJECT_NAME to automate
 * static(allocation-less) construction.
 *
 * note: I assume CPOLITE_H is included; since if I use anonymous structs
 *     for each object, they become incompatible and cause compile time errors
 *     when trying to do stuff like assign, or pass functions.
 *     this is similar to how you can't pass void * to windows functions that
 *         take handles; these handles use anonymous structs to make 
 *         HWND/HANDLE/HINSTANCE/void*/etc not automatically convertible, and
 *         require a cast.
 */
#ifndef OBJECT_NAME
    #error CCPolite> constructor requires object name.
#endif

CPolite OBJECT_NAME = {
    &CCPolite_Vtbl
};

/* ensure no global scope pollution */
#undef OBJECT_NAME

main.c :

#include <stdio.h>
#include "CCPolite.h"

// | A Greeter is capable of greeting; nothing else.
struct IGreeter
{
    virtual void Greet() = 0;
};

// | A Thanker is capable of thanking; nothing else.
struct IThanker
{
    virtual void Thank() = 0;
};

// | A Polite is something that implements both IGreeter and IThanker
// | Note that order of implementation DOES MATTER.
struct IPolite1 : public IGreeter, public IThanker{};
struct IPolite2 : public IThanker, public IGreeter{};

// | implementation if IPolite1; implements IGreeter BEFORE IThanker
struct CPolite1 : public IPolite1
{
    void Greet()
    {
        puts("hello!");
    }

    void Thank()
    {
        puts("thank you!");
    }
};

// | implementation if IPolite1; implements IThanker BEFORE IGreeter
struct CPolite2 : public IPolite2
{
    void Greet()
    {
        puts("hi!");
    }

    void Thank()
    {
        puts("ty!");
    }
};

// | imposter Polite's Greet implementation.
static void CCPolite_Greet(void *)
{
    puts("HI I AM C!!!!");
}

// | imposter Polite's Thank implementation.
static void CCPolite_Thank(void *)
{
    puts("THANK YOU, I AM C!!");
}

// | vtable of the imposter Polite.
ICCPolite CCPolite_Vtbl = {
    CCPolite_Thank,
    CCPolite_Greet    
};

CPolite CCPoliteObj = {
    &CCPolite_Vtbl
};

int main(int argc, char **argv)
{
    puts("\npart 1");
    CPolite1 o1;
    o1.Greet();
    o1.Thank();

    puts("\npart 2");    
    CPolite2 o2;    
    o2.Greet();
    o2.Thank();    

    puts("\npart 3");    
    CPolite1 *not1 = (CPolite1 *)&o2;
    CPolite2 *not2 = (CPolite2 *)&o1;
    not1->Greet();
    not1->Thank();
    not2->Greet();
    not2->Thank();

    puts("\npart 4");        
    CPolite1 *fake = (CPolite1 *)&CCPoliteObj;
    fake->Thank();
    fake->Greet();

    puts("\npart 5");        
    CPolite2 *fake2 = (CPolite2 *)fake;
    fake2->Thank();
    fake2->Greet();

    puts("\npart 6");        
    #define OBJECT_NAME fake3
    #include "CCPolite_constructor.h"
    fake = (CPolite1 *)&fake3;
    fake->Thank();
    fake->Greet();

    puts("\npart 7");        
    #define OBJECT_NAME fake4
    #include "CCPolite_constructor.h"
    fake2 = (CPolite2 *)&fake4;
    fake2->Thank();
    fake2->Greet();    

    return 0;
}

আউটপুট:

part 1
hello!
thank you!

part 2
hi!
ty!

part 3
ty!
hi!
thank you!
hello!

part 4
HI I AM C!!!!
THANK YOU, I AM C!!

part 5
THANK YOU, I AM C!!
HI I AM C!!!!

part 6
HI I AM C!!!!
THANK YOU, I AM C!!

part 7
THANK YOU, I AM C!!
HI I AM C!!!!

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.