একক ভার্চুয়াল ফাংশনটি কি পুরো ক্লাসকে ধীর করে দেয়?
বা ভার্চুয়াল যে ফাংশন শুধুমাত্র কল? এবং যদি ভার্চুয়াল ফাংশনটি আসলে ওভাররাইট করা থাকে বা গতিটি প্রভাবিত হয়, বা ভার্চুয়াল হওয়ার কারণে এর কোনও প্রভাব নেই?
ভার্চুয়াল ফাংশনগুলি হ'ল পুরো শ্রেণীর ইনসোফারকে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে নামিয়ে আনতে হবে। অর্ধ ডজন সদস্য বা এর বেশি সহ একটি শ্রেণীর জন্য, পার্থক্যটি অবহেলা করা উচিত। একটি শ্রেণীর জন্য যেখানে কেবলমাত্র একক 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
সঠিকভাবে টাইপ করা এক্সপ্রেশন হোক।
Inside the C++ Object Model
দ্বারাStanley B. Lippman
। (বিভাগ 4.2, পৃষ্ঠা 124-131)