উদ্ভূত শ্রেণিতে ফাংশনগুলির জন্য সি ++ "ভার্চুয়াল" কীওয়ার্ড। এটা কি দরকারি?


221

নীচে দেওয়া কাঠামো সংজ্ঞা সহ ...

struct A {
    virtual void hello() = 0;
};

পন্থা # 1:

struct B : public A {
    virtual void hello() { ... }
};

পদ্ধতির # 2:

struct B : public A {
    void hello() { ... }
};

হ্যালো ফাংশনটিকে ওভাররাইড করার জন্য এই দুটি পদ্ধতির মধ্যে কোনও পার্থক্য রয়েছে কি?


65
সি ++ 11 এ আপনি স্পষ্টভাবে ঘোষণা করতে পারেন যে আপনি ভার্চুয়াল পদ্ধতিতে ওভাররাইড করছেন। একটি বেস ভার্চুয়াল পদ্ধতি উপস্থিত না থাকলে সংকলকটি ব্যর্থ হবে এবং বংশধর শ্রেণিতে "ভার্চুয়াল" রাখার মতোই পঠনযোগ্যতা রয়েছে।
শ্যাডোচ্যাজার

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

উত্তর:


183

তারা ঠিক একই আছে। প্রথম পদ্ধতির জন্য আরও টাইপিংয়ের প্রয়োজন রয়েছে এবং এটি সম্ভবত আরও পরিষ্কার than


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

5
আমি এটিকে যুক্ত করে দেব যা স্পষ্টভাবে এটি ভার্চুয়াল হিসাবে চিহ্নিত করা আপনাকে ডিস্ট্রাক্টরকে ভার্চুয়াল করার জন্য মনে করিয়ে দেবে।
lfalin

1
কেবল উল্লেখ করার জন্য, ভার্চুয়াল ডেস্ট্রাক্টরের
অতুল

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

4
@ রসমি, নতুন বহনযোগ্যতা গাইড এখানে রয়েছে তবে এখন এটি overrideকীওয়ার্ডটি ব্যবহার করার পরামর্শ দিচ্ছে ।
সের্গেই তাচেনভ

83

কোনও ক্রিয়াকলাপের 'ভার্চুয়ালনেস' সুস্পষ্টভাবে প্রচারিত হয়, তবে আমি ব্যবহার করি কমপক্ষে একটি সংকলক একটি সতর্কতা উত্পন্ন করবে যদি মূল virtualশব্দটি স্পষ্টভাবে ব্যবহার না করা হয়, তাই আপনি কেবল সংকলককে চুপ করে রাখলে আপনি এটি ব্যবহার করতে চাইতে পারেন।

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


12
এটি কোন সংকলক?
জেমস ম্যাকনেলিস

35
@ জেমস: আর্মসিসি (এআরএম ডিভাইসের জন্য এআরএম এর সংকলক)
ক্লিফোর্ড

55

virtualশব্দ উদ্ভূত ক্লাসে প্রয়োজন নেই। সি ++ ড্রাফ্ট স্ট্যান্ডার্ড (এন 333) (জোর খনির) থেকে সহায়ক ডকুমেন্টেশন এখানে রয়েছে:

10.3 ভার্চুয়াল ফাংশন

2 একটি ভার্চুয়াল সদস্য ফাংশন যদি vfকোন ক্লাসে ঘোষিত হয় Baseএবং কোন ক্লাসে Derivedপ্রত্যক্ষ বা পরোক্ষভাবে উদ্ভূত থেকে Base, একজন সদস্য ফাংশন vfএকই নামের, প্যারামিটার-টাইপ-তালিকা (8.3.5), CV-যোগ্যতা, এবং রেফ-কোয়ালিফায়ার ( বা এর অনুপস্থিতি হিসাবে Base::vf) ঘোষিত হয়েছে, তবে Derived::vfএটি ভার্চুয়ালও ( এটি এতটা ঘোষিত হয়েছে কিনা ) এবং এটি ওভাররাইড করে Base::vf


5
এটি এখানকার সেরা উত্তর।
ফ্যান্টাস্টিক মিঃ ফক্স

33

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

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

এই ক্ষতিটি অবশ্য, C ++ 11 স্পষ্টত ওভাররাইড ভাষা বৈশিষ্ট্য দ্বারা কৃতজ্ঞতার সাথে সম্বোধন করা হয়েছে , যা উত্স কোডটি পরিষ্কারভাবে নির্দিষ্ট করতে দেয় যে কোনও সদস্য ফাংশনটি একটি বেস শ্রেণীর ফাংশনকে ওভাররাইড করার উদ্দেশ্যে তৈরি করা হয়েছে:

struct Base {
    virtual void some_func(float);
};

struct Derived : Base {
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

সংকলকটি একটি সংকলন-সময় ত্রুটি জারি করবে এবং প্রোগ্রামিং ত্রুটিটি তাত্ক্ষণিকভাবে সুস্পষ্ট হয়ে উঠবে (সম্ভবত ডেরাইভের ক্রিয়াকলাপটি floatযুক্তি হিসাবে গ্রহণ করা উচিত )।

পড়ুন ডাব্লু: সি ++ 11


11

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


7

সংকলকটির জন্য কোনও পার্থক্য নেই, যখন আপনি virtualউত্পন্ন শ্রেণিতে লেখেন বা বাদ দেন।

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


2

আপনার যখন টেম্পলেট রয়েছে এবং বেস ক্লাস (এস) টেম্পলেট প্যারামিটার হিসাবে নেওয়া শুরু হয় তখন যথেষ্ট পার্থক্য থাকে:

struct None {};

template<typename... Interfaces>
struct B : public Interfaces
{
    void hello() { ... }
};

struct A {
    virtual void hello() = 0;
};

template<typename... Interfaces>
void t_hello(const B<Interfaces...>& b) // different code generated for each set of interfaces (a vtable-based clever compiler might reduce this to 2); both t_hello and b.hello() might be inlined properly
{
    b.hello();   // indirect, non-virtual call
}

void hello(const A& a)
{
    a.hello();   // Indirect virtual call, inlining is impossible in general
}

int main()
{
    B<None>  b;         // Ok, no vtable generated, empty base class optimization works, sizeof(b) == 1 usually
    B<None>* pb = &b;
    B<None>& rb = b;

    b.hello();          // direct call
    pb->hello();        // pb-relative non-virtual call (1 redirection)
    rb->hello();        // non-virtual call (1 redirection unless optimized out)
    t_hello(b);         // works as expected, one redirection
    // hello(b);        // compile-time error


    B<A>     ba;        // Ok, vtable generated, sizeof(b) >= sizeof(void*)
    B<None>* pba = &ba;
    B<None>& rba = ba;

    ba.hello();         // still can be a direct call, exact type of ba is deducible
    pba->hello();       // pba-relative virtual call (usually 3 redirections)
    rba->hello();       // rba-relative virtual call (usually 3 redirections unless optimized out to 2)
    //t_hello(b);       // compile-time error (unless you add support for const A& in t_hello as well)
    hello(ba);
}

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

দ্রষ্টব্য, আপনি যদি এটি করেন তবে আপনি অনুলিপি / মুভ কনস্ট্রাক্টরকেও টেমপ্লেট হিসাবে ঘোষণা করতে চাইতে পারেন: বিভিন্ন ইন্টারফেস থেকে নির্মাণের অনুমতি আপনাকে বিভিন্ন B<>ধরণের মধ্যে 'কাস্ট' করতে দেয় ।

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


1

virtualশব্দ একটি বেস ক্লাসের ফাংশন যোগ করা তাদের overridable করা। আপনার উদাহরণে, struct Aবেস ক্লাস হয়। virtualএকটি উদ্ভূত শ্রেণিতে এই ফাংশনগুলি ব্যবহার করার জন্য কিছুই বোঝায় না। যাইহোক, এটি আপনি চান যে আপনার উত্পন্ন শ্রেণিটি নিজেও একটি বেস বর্গ হতে পারে, এবং আপনি চান যে ফাংশনটি অতিমাত্রায়িত হতে পারে, তারপরে আপনাকে virtualসেখানে রাখতে হবে।

struct B : public A {
    virtual void hello() { ... }
};

struct C : public B {
    void hello() { ... }
};

এখানে Cউত্তরাধিকার সূত্রে প্রাপ্ত B, সুতরাং Bবেস শ্রেণিটি নয় (এটি একটি উদ্ভূত বর্গও বটে), এবং Cএটি উত্পন্ন শ্রেণি। উত্তরাধিকার চিত্রটি দেখতে এরকম দেখাচ্ছে:

A
^
|
B
^
|
C

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

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


0

আমি অবশ্যই শিশু শ্রেণীর জন্য ভার্চুয়াল কীওয়ার্ডটি অন্তর্ভুক্ত করব, কারণ

  • আমি। পঠনযোগ্যতার।
  • আ। এই শিশু শ্রেণিটি আমার আরও নীচে নেমে আসে, আপনি চান না যে আরও উত্পন্ন ক্লাসের নির্মাতা এই ভার্চুয়াল ফাংশনটিকে কল করুন।

1
আমি মনে করি তার অর্থ হ'ল শিশু ফাংশনটিকে ভার্চুয়াল হিসাবে চিহ্নিত না করে, একজন প্রোগ্রামার যিনি পরে শিশু বর্গ থেকে উদ্ভূত হন তা বুঝতে পারেন না যে ফাংশনটি আসলে ভার্চুয়াল (কারণ তিনি কখনই বেস ক্লাসের দিকে নজর দেননি) এবং নির্মাণের সময় এটি সম্ভবত কল করতে পারেন ( যা সঠিক কাজ করতে পারে বা নাও করতে পারে)।
PfhorSlayer
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.