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


143

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


30
" আপনার" চূড়ান্ত "ফাংশনগুলি অ-ভার্চুয়াল হিসাবে ঘোষণা করার পক্ষে যথেষ্ট নয় " না, আপনি virtualকীওয়ার্ডটি ব্যবহার করেন কিনা তা ওভাররাইডিং ফাংশনগুলি পুরোপুরি ভার্চুয়াল ।
iljarn

13
@ildjarn এটি সত্য নয় যদি তাদের সুপার ক্লাসে ভার্চুয়াল হিসাবে ঘোষণা না করা হয়, আপনি কোনও শ্রেণি থেকে উদ্ভূত হতে পারেন এবং একটি ভার্চুয়াল পদ্ধতিতে একটি নন-ভার্চুয়াল পদ্ধতিতে রূপান্তর করতে পারবেন না
ড্যান ও

10
@ ড্যানো আমি মনে করি আপনি ওভাররাইড করতে পারবেন না তবে আপনি কোনও পদ্ধতি "গোপন" করতে পারেন .. যা অনেক সমস্যার দিকে নিয়ে যায় কারণ লোকেরা পদ্ধতিগুলি আড়াল করার অর্থ নয়।
অ্যালেক্স ক্রেমার

16
@ ড্যানো: যদি এটি সুপার ক্লাসে ভার্চুয়াল না হয় তবে এটি "ওভাররাইডিং" হবে না।
iljarn

2
আবার, " ওভাররাইডিং " এর এখানে একটি নির্দিষ্ট অর্থ রয়েছে যা ভার্চুয়াল ফাংশনটিতে বহুবর্ষাত্মক আচরণ দেওয়া। আপনার উদাহরণ funcভার্চুয়াল নয়, তাই ওভাররাইড কিছুই এবং এইভাবে হিসেবে চিহ্নিত করতে কিছু নয় overrideবা final
iljarn

উত্তর:


129

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

struct base {
   virtual void f();
};
struct derived : base {
   void f() final;       // virtual as it overrides base::f
};
struct mostderived : derived {
   //void f();           // error: cannot override!
};

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

8
@ লেজেবুলন: যদি সুপার ক্লাসটি ভার্চুয়াল হিসাবে ঘোষণা করে তবে আপনার পাতার ক্লাসগুলিকে কোনও ফাংশনটিকে ভার্চুয়াল হিসাবে চিহ্নিত করার দরকার নেই।
ড্যান ও

5
পাতাগুলির ক্লাসগুলির পদ্ধতিগুলি বেস ক্লাসে ভার্চুয়াল হলে পুরোপুরি ভার্চুয়াল। আমি মনে করি যে এই অন্তর্ভুক্ত 'ভার্চুয়াল' অনুপস্থিত থাকলে সংকলকগণকে সতর্ক করা উচিত।
অ্যারন ম্যাকডেইড

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

1
জিসিসি থেকে 9.৯ পরিবর্তন করুন নোট: "নতুন ধরণের উত্তরাধিকার বিশ্লেষণ মডিউলটি ডেভার্টুয়ালাইজেশনের উন্নতি করে Dev ডেভেরচুয়ালাইজেশন এখন বেনামে নাম-স্পেস এবং সি ++ ১১ চূড়ান্ত কীওয়ার্ড বিবেচনা করে" - সুতরাং এটি কেবল সিনট্যাকটিক চিনি নয়, এর সম্ভাব্য অপ্টিমাইজেশন সুবিধাও রয়েছে।
কেএফসনে

126
  • এটি কোনও শ্রেণীর উত্তরাধিকার সূত্রে বাধা দেওয়া। উইকিপিডিয়া থেকে :

    সি ++ 11 ক্লাস থেকে উত্তরাধিকার সূত্রে প্রাপ্ত হওয়া বা উত্পন্ন ক্লাসগুলিতে কেবল ওভাররাইড পদ্ধতিগুলি রোধ করার ক্ষমতাও যুক্ত করে। এটি বিশেষ সনাক্তকারী ফাইনাল দিয়ে সম্পন্ন হয়। উদাহরণ স্বরূপ:

    struct Base1 final { };
    
    struct Derived1 : Base1 { }; // ill-formed because the class Base1 
                                 // has been marked final
  • এটি কোনও ভার্চুয়াল ফাংশন চিহ্নিত করতে ব্যবহার করা হয় যাতে এটি উত্পন্ন ক্লাসগুলিতে ওভাররাইড হওয়া থেকে রোধ করতে পারে:

    struct Base2 {
        virtual void f() final;
    };
    
    struct Derived2 : Base2 {
        void f(); // ill-formed because the virtual function Base2::f has 
                  // been marked final
    };

উইকিপিডিয়া আরও একটি আকর্ষণীয় বিষয় তোলে :

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

তার অর্থ, নিম্নলিখিত অনুমোদিত:

int const final = 0;     // ok
int const override = 1;  // ok

1
ধন্যবাদ, তবে আমি উল্লেখ করতে ভুলে গেছি যে আমার প্রশ্ন পদ্ধতিগুলির সাথে "চূড়ান্ত" ব্যবহারের সাথে সম্পর্কিত
লেজবুলন

আপনি এটি @ লেজেবুলন উল্লেখ করেছেন :-) " ফাংশনগুলির জন্য সি ++ 11 এ" চূড়ান্ত "কীওয়ার্ডটির উদ্দেশ্য কী "। (আমার জোর)
অ্যারন ম্যাকডেইড

আপনি এটি সম্পাদনা করেছেন? আমি "এমন কোনও বার্তা দেখতে পাচ্ছি না যা" লিজেবুলন দ্বারা x মিনিট আগে সম্পাদিত "বলে। তা কীভাবে হল? আপনি এটি জমা দেওয়ার পরে খুব দ্রুত সম্পাদনা করেছেন?
অ্যারন ম্যাকডেইড

5
@ অ্যারন: পোস্টের পাঁচ মিনিটের মধ্যে সম্পাদিত সম্পাদনাগুলি পুনর্বিবেচনার ইতিহাসে প্রতিফলিত হয় না।
iljarn

@ নাওয়াজ: কেন তারা কীওয়ার্ডগুলি কেবল নির্দিষ্টকারক নয়? সামঞ্জস্যতার কারণে এটির অর্থ কি এটি সম্ভব যে সি ++ 11 এর আগে অন্য উদ্দেশ্যে চূড়ান্ত ও ওভাররাইড ব্যবহারের আগেই প্রিক্সিং কোডটি সম্ভব?
ধ্বংসকারী

45

"চূড়ান্ত" অপ্রত্যক্ষ কলকে বাইপাস করার জন্য একটি সংকলক অপ্টিমাইজেশনকে মঞ্জুরি দেয়:

class IAbstract
{
public:
  virtual void DoSomething() = 0;
};

class CDerived : public IAbstract
{
  void DoSomething() final { m_x = 1 ; }

  void Blah( void ) { DoSomething(); }

};

"চূড়ান্ত" দিয়ে, সংকলকটি CDerived::DoSomething()সরাসরি ভিতরে Blah()বা এমনকি ইনলাইন থেকে কল করতে পারে। এটি ছাড়াই, এটির ভিতরে একটি পরোক্ষ কল উত্পন্ন করতে হবে Blah()কারণ Blah()একটি উত্পন্ন শ্রেণীর ভিতরে কল করা যেতে পারে যা ওভাররাইড হয়ে গেছে DoSomething()


29

"চূড়ান্ত" এর অর্থগত দিকগুলিতে যুক্ত করার মতো কিছুই নেই।

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

ভেটেবলগুলির একটি প্রধান অসুবিধা হ'ল এই জাতীয় কোনও ভার্চুয়াল অবজেক্টের জন্য (একটি সাধারণ ইন্টেল সিপিইউতে -৪-বিট ধরে) কেবলমাত্র পয়েন্টার একটি ক্যাশে লাইনের 25% (64 বাইটের 8 টি) খায়। আমি লেখার জন্য যে ধরণের অ্যাপ্লিকেশন উপভোগ করি তাতে খুব খারাপ লাগে। (এবং আমার অভিজ্ঞতা থেকে এটি পিউরিস্ট পারফরম্যান্স দৃষ্টিকোণ, অর্থাৎ সি প্রোগ্রামারদের দ্বারা সি ++ এর বিপরীতে # 1 টি যুক্তি))

যে অ্যাপ্লিকেশনগুলিতে চূড়ান্ত পারফরম্যান্সের প্রয়োজন হয়, যা সি ++ এর জন্য এতটা অস্বাভাবিক নয়, এটি সম্ভবত দুর্দান্ত হয়ে উঠবে, সি স্টাইল বা অদ্ভুত টেম্পলেট জাগলিংয়ে ম্যানুয়ালি এই সমস্যাটির প্রয়োজন নেই work

এই কৌশলটি ডেভ্যাচারুয়ালাইজেশন হিসাবে পরিচিত । মনে রাখার মতো একটি পদ। :-)

আন্দ্রে আলেকজান্দ্রেস্কুর একটি সাম্প্রতিক বক্তব্য রয়েছে যা আপনাকে আজ কীভাবে এমন পরিস্থিতিগুলি সমাধান করতে পারে এবং ভবিষ্যতে "স্বয়ংক্রিয়ভাবে" অনুরূপ মামলাগুলি সমাধানের অংশ হতে পারে "চূড়ান্ত" হতে পারে তা খুব সুন্দরভাবে ব্যাখ্যা করেছে (শ্রোতাদের সাথে আলোচিত):

http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly


23
8 এর 25% কি?
iljarn

6
এমন একটি সংকলক জানেন যে এখন সেগুলি ব্যবহার করে?
ভিনসেন্ট ফোরমন্ড

আমি বলতে চাই একই জিনিস।
crazii

8

চূড়ান্তটি নন-ভার্চুয়াল ফাংশনে প্রয়োগ করা যাবে না।

error: only virtual member functions can be marked 'final'

কোনও ভার্চুয়াল পদ্ধতিটিকে 'চূড়ান্ত' হিসাবে চিহ্নিত করতে সক্ষম হওয়া খুব অর্থবহ হবে না। প্রদত্ত

struct A { void foo(); };
struct B : public A { void foo(); };
A * a = new B;
a -> foo(); // this will call A :: foo anyway, regardless of whether there is a B::foo

a->foo()সবসময় কল করবে A::foo

তবে, যদি এ :: ফু ছিল virtual, তবে বি :: ফু এটি ওভাররাইড করবে। এটি অনাকাঙ্ক্ষিত হতে পারে এবং তাই ভার্চুয়াল ফাংশনটিকে চূড়ান্ত করে তোলা বুদ্ধিমান হয়ে উঠবে।

যদিও প্রশ্নটি হল, ভার্চুয়াল ফাংশনগুলিতে চূড়ান্ত কেন অনুমতি দেওয়া। আপনার যদি গভীর স্তরক্রম থাকে:

struct A            { virtual void foo(); };
struct B : public A { virtual void foo(); };
struct C : public B { virtual void foo() final; };
struct D : public C { /* cannot override foo */ };

তারপরে finalকতটা ওভাররাইড করা যায় তার উপর একটি 'তল' রাখে। অন্যান্য শ্রেণি A এবং B প্রসারিত করতে পারে এবং তাদের ওভাররাইড করতে পারে fooতবে এটি একটি শ্রেণি C এর পরে প্রসারিত করে তবে এটি অনুমোদিত নয়।

সুতরাং সম্ভবত এটি 'শীর্ষ-স্তরের' ফু তৈরি করার কোনও অর্থ নেই final, তবে এটি নীচের দিকে বুদ্ধিমান হতে পারে।

(যদিও আমি মনে করি, শব্দগুলি চূড়ান্তভাবে প্রসারিত করার জন্য এবং নন-ভার্চুয়াল সদস্যদের কাছে ওভাররাইড করার সুযোগ রয়েছে They যদিও তাদের আলাদা অর্থ থাকবে))


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

@ লেজেবুলন, আমি আমার প্রশ্ন সম্পাদনা করেছি। কিন্তু তখন আমি ড্যানোর উত্তর লক্ষ্য করেছি - আমি যা বলার চেষ্টা করেছিলাম এটির এটির একটি পরিষ্কার উত্তর।
অ্যারন ম্যাকডেইড

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

8

'চূড়ান্ত' কীওয়ার্ডের জন্য আমি ব্যবহারের ক্ষেত্রে ব্যবহারের বিষয়টি নিম্নরূপ:

// This pure abstract interface creates a way
// for unit test suites to stub-out Foo objects
class FooInterface
{
public:
   virtual void DoSomething() = 0;
private:
   virtual void DoSomethingImpl() = 0;
};

// Implement Non-Virtual Interface Pattern in FooBase using final
// (Alternatively implement the Template Pattern in FooBase using final)
class FooBase : public FooInterface
{
public:
    virtual void DoSomething() final { DoFirst(); DoSomethingImpl(); DoLast(); }
private:
    virtual void DoSomethingImpl() { /* left for derived classes to customize */ }
    void DoFirst(); // no derived customization allowed here
    void DoLast(); // no derived customization allowed here either
};

// Feel secure knowing that unit test suites can stub you out at the FooInterface level
// if necessary
// Feel doubly secure knowing that your children cannot violate your Template Pattern
// When DoSomething is called from a FooBase * you know without a doubt that
// DoFirst will execute before DoSomethingImpl, and DoLast will execute after.
class FooDerived : public FooBase
{
private:
    virtual void DoSomethingImpl() {/* customize DoSomething at this location */}
};

1
হ্যাঁ, এটি মূলত টেম্পলেট পদ্ধতি প্যাটার্নের একটি উদাহরণ। এবং প্রাক-সি ++ 11, এটি সর্বদা টিএমপিই ছিল যে আমার ইচ্ছা ছিল যে সি ++ "জাভা'র মতো" ফাইনাল "এর মতো কোনও ভাষা বৈশিষ্ট্য রাখে।
কাইতাইন

6

final আপনার ফাংশনটি ওভাররাইড না করার জন্য একটি সুস্পষ্ট অভিপ্রায় যুক্ত করেছে এবং এটি লঙ্ঘন করা উচিত এমন একটি সংকলক ত্রুটির কারণ ঘটবে:

struct A {
    virtual int foo(); // #1
};
struct B : A {
    int foo();
};

কোডটি দাঁড়ানোর সাথে সাথে এটি সংকলিত হয় এবং B::fooওভাররাইড হয় A::fooB::fooউপায় দ্বারা, ভার্চুয়াল হয়। তবে, আমরা যদি # 1 তে পরিবর্তন করি তবে virtual int foo() finalএটি একটি সংকলক ত্রুটি, এবং আমাদের A::fooউত্পন্ন ক্লাসগুলিতে আর কোনও ওভাররাইড করার অনুমতি নেই ।

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


5

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

struct Foo
{
   virtual void DoStuff();
}

struct Bar : public Foo
{
   void DoStuff() final;
}

struct Babar : public Bar
{
   void DoStuff(); // error!
}

অন্যান্য পোস্টারগুলি উল্লেখ করেছে যে এটি অ-ভার্চুয়াল ফাংশনে প্রয়োগ করা যাবে না।

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


1

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

#include <iostream>
using namespace std;

class Base
{
  public:
  virtual void myfun() final
  {
    cout << "myfun() in Base";
  }
};
class Derived : public Base
{
  void myfun()
  {
    cout << "myfun() in Derived\n";
  }
};

int main()
{
  Derived d;
  Base &b = d;
  b.myfun();
  return 0;
}

এছাড়াও:

#include <iostream>
class Base final
{
};

class Derived : public Base
{
};

int main()
{
  Derived d;
  return 0;
}

0

মারিও Kenzović এর উত্তর পরিপূরক:

class IA
{
public:
  virtual int getNum() const = 0;
};

class BaseA : public IA
{
public:
 inline virtual int getNum() const final {return ...};
};

class ImplA : public BaseA {...};

IA* pa = ...;
...
ImplA* impla = static_cast<ImplA*>(pa);

//the following line should cause compiler to use the inlined function BaseA::getNum(), 
//instead of dynamic binding (via vtable or something).
//any class/subclass of BaseA will benefit from it

int n = impla->getNum();

উপরের কোডটি তত্ত্বটি দেখায়, তবে প্রকৃত সংকলকগুলিতে আসলে পরীক্ষিত হয় না। যদি কেউ একটি বিচ্ছিন্ন আউটপুট পেস্ট করে তবে অনেক প্রশংসা করা হয়েছে।

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