সি ++ স্ট্যাটিক ভার্চুয়াল সদস্য?


140

সদস্য ফাংশন উভয় যে আছে সি এটা কি সম্ভব ++, staticএবং virtual? স্পষ্টতই, এটি করার সহজ সরল উপায় নেই ( static virtual member();এটি একটি সংকলন ত্রুটি), তবে একই প্রভাব অর্জন করার জন্য কি কমপক্ষে কোনও উপায় আছে?

অর্থাৎ,

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

এটি GetTypeInformation()উদাহরণ ( object->GetTypeInformation()) এবং একটি শ্রেণিতে ( SomeObject::GetTypeInformation()) উভয়ই ব্যবহার করা বোধগম্য হয় , যা তুলনার জন্য দরকারী এবং টেমপ্লেটগুলির জন্য অত্যাবশ্যক।

আমি কেবলমাত্র দুটি উপায় সম্পর্কে লিখতে পারি তাতে দুটি ফাংশন / একটি ফাংশন এবং একটি ধ্রুবক, প্রতি ক্লাসে লেখা বা ম্যাক্রোগুলি ব্যবহার করা জড়িত।

অন্য কোন সমাধান?


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

2
@ সিভিবি: আমি আপনার উদাহরণটি এমন কোডের সাথে প্রতিস্থাপনের সাথে গুরুত্ব সহকারে পুনর্বিবেচনা করব। এখন যেভাবে আপনি দুটি পৃথক (সম্পর্কিত যাই হোক না কেন) সমস্যা গুলো সাজানোর ধরণের। হ্যাঁ, এবং আমি জানি আপনি এটি জিজ্ঞাসা করার সাড়ে পাঁচ বছর পরে years
einpoklum

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

উত্তর:


75

না, এটি করার কোনও উপায় নেই, যেহেতু আপনি যখন ফোন করবেন তখন কি হবে Object::GetTypeInformation()? এটি সম্পর্কিত কোনও শ্রেণীর সংস্করণটি কল করতে পারে না কারণ এর সাথে কোনও বস্তু যুক্ত নেই।

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


8
আপনি যদি স্ট্যাটিক ক্লাস (বা ক্লাস স্ট্যাটিক সদস্য) কে সিঙ্গলটন হিসাবে মনে করেন তবে সবকিছু সুস্পষ্ট হয়ে যায় - আপনার ক্ষেত্রে কেবল অবজেক্ট :: গেটটাইপআইনফর্মেশন বলা উচিত - বেস ক্লাসের উদাহরণগুলিতে নিয়মিত ভার্চুয়াল পদ্ধতিতে কল করার মতোই । (অবশ্যই, যদি সি ++ সমর্থিত ভার্চুয়াল স্ট্যাটিক পদ্ধতি)
ভূত

13
এটি একটি সম্পূর্ণ উদ্বেগ যুক্তি। আপনি যদি কোনও বস্তুর পরিবর্তে ক্লাস ব্যবহার করেন তবে এটি ভার্চুয়াল-প্রেরণের পরিবর্তে স্বাভাবিকভাবে class শ্রেণীর সংস্করণটি ব্যবহার করবে। সেখানে নতুন কিছু নেই।
উত্সাহক

54

অনেকে বলে যে এটি সম্ভব নয়, আমি একধাপ এগিয়ে যেতে চাই এবং বলি এটি অর্থপূর্ণ নয়।

একটি স্থিতিশীল সদস্য এমন কিছু যা কোনও শ্রেণীর সাথে সম্পর্কিত নয়, কেবল শ্রেণীর সাথে।

ভার্চুয়াল সদস্য এমন একটি জিনিস যা কোনও শ্রেণীর সাথে সরাসরি সম্পর্কিত হয় না, কেবলমাত্র একটি উদাহরণের সাথে।

সুতরাং একটি স্ট্যাটিক ভার্চুয়াল সদস্য এমন কিছু হবে যা কোনও উদাহরণ বা কোনও শ্রেণীর সাথে সম্পর্কিত নয়।


42
এটি এমন ভাষাগুলিতে পুরোপুরি অর্থবহ, যেখানে ক্লাসগুলি প্রথম শ্রেণীর মান -
পাভেল মিনায়েভ

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

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

34
এটি কোনও static virtualপদ্ধতির জন্য এতটা অর্থবহ নয় , তবে একটি ইন্টারফেসে একটি static খাঁটি virtual পদ্ধতি খুব অর্থবহ।
ব্রেট কুহন্স

4
এটি থাকা পুরোপুরি অর্থপূর্ণ static const string MyClassSillyAdditionalName
einpoklum

23

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

স্থির পদ্ধতি ব্যবহারের পরিবর্তে ভার্চুয়াল পদ্ধতিগুলির সাথে একটি সিঙ্গলটন ব্যবহার করুন।

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

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


এটি আমার পারফরম্যান্সের জন্য ব্যয় করতে চলেছে - যদি না সংকলকটি নিশ্চিত না হতে পারে: 1. এটি আসলে একটি সিঙ্গলটন এবং ২. এর থেকে উত্তরাধিকারসূত্রে কিছুই আসে না, আমি ভাবি না যে এটি ওভারহেডের সমস্ত দূরে সরিয়ে নিতে পারে can
einpoklum

যদি এই ধরণের জিনিসটির পারফরম্যান্স আপনাকে উদ্বেগ দেয় তবে সি # সম্ভবত আপনার পক্ষে ভুল ভাষা।
নেট সি কে

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

15

এটা সম্ভব!

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

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

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

সুতরাং, আমরা প্রাপ্ত উত্পন্ন শ্রেণীর প্রতি getTypeInformation () এর একটিমাত্র সংজ্ঞা সরবরাহ করতে চাই এবং এটি স্পষ্টতই এটির একটি সংজ্ঞা হতে হবে স্থিরফাংশন কারণ getTypeInفارمেশন () ভার্চুয়াল হলে "SomeDerivedClass :: getTypeInformation ()" কল করা সম্ভব নয় function আমরা কিভাবে বেস ক্লাসে পয়েন্টারের মাধ্যমে উত্পন্ন শ্রেণীর স্থিতিকর ফাংশন বলতে পারি? ভেটেবলের মাধ্যমে এটি সম্ভব নয় কারণ ভেটেবল কেবলমাত্র ভার্চুয়াল ফাংশনগুলিতে নির্দেশ করে এবং যেহেতু আমরা ভার্চুয়াল ফাংশনগুলি ব্যবহার না করার সিদ্ধান্ত নিয়েছি, আমরা আমাদের উপকারের জন্য ভেটেবল পরিবর্তন করতে পারি না। তারপরে, পয়েন্টার থেকে বেস ক্লাসে উত্পন্ন শ্রেণীর জন্য স্ট্যাটিক ফাংশন অ্যাক্সেস করতে সক্ষম হওয়ার জন্য আমাদের কোনওভাবে তার বেস বর্গের মধ্যে কোনও বস্তুর প্রকার সংরক্ষণ করতে হবে। একটি পদ্ধতি হ'ল "কৌতূহলীভাবে পুনরাবৃত্তি টেম্পলেট প্যাটার্ন" ব্যবহার করে বেস বর্গকে অস্থির করে তোলা তবে এটি এখানে উপযুক্ত নয় এবং আমরা "টাইপ ইরেজর" নামক একটি প্রযুক্তি ব্যবহার করব:

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

এখন আমরা বেস শ্রেণীর "অবজেক্ট" এর মধ্যে একটি ভেরিয়েবল "রক্ষক" দিয়ে কোনও বস্তুর প্রকার সংরক্ষণ করতে পারি:

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

একটি উত্পন্ন শ্রেণিতে রক্ষককে নির্মাণের সময় অবশ্যই শুরু করতে হবে:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

আসুন সিনট্যাকটিক চিনি যুক্ত করুন:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

এখন বংশধরদের ঘোষণাগুলি দেখতে এ রকম:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

ব্যবহার:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

সুবিধাদি:

  1. কোডের কম অনুলিপি (তবে আমাদের প্রতিটি কনস্ট্রাক্টরে OVERRIDE_STATIC_FUNCTIONS কল করতে হবে)

অসুবিধা:

  1. প্রতিটি কনস্ট্রাক্টরে OVERRIDE_STATIC_FUNCTIONS
  2. মেমরি এবং কর্মক্ষমতা ওভারহেড
  3. জটিলতা বৃদ্ধি

এমনকি আপনি যদি:

1) স্থির এবং ভার্চুয়াল ফাংশনগুলির জন্য আলাদা আলাদা নাম রয়েছে এখানে অস্পষ্টতা কীভাবে সমাধান করা যায়?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) প্রতিটি কনস্ট্রাক্টরের ভিতরে কীভাবে স্পষ্টত OVERRIDE_STATIC_FUNCTIONS কল করতে হয়?


প্রয়াসের জন্য +1, যদিও আমি নিশ্চিত নই যে এটি ভার্চুয়াল পদ্ধতিগুলির সাথে একটি সিঙ্গলটনে কার্যকারিতাটি অর্পণ করার চেয়ে আরও মার্জিত।
einpoklum

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

"Foo :: getTypeInformation" এবং "TypeKeeperImpl :: getTypeInformation" এর জন্য "ভার্চুয়াল" কীওয়ার্ডের প্রয়োজন নেই।
বার্টোলো-ওট্রিট

12

যদিও অ্যালস্ক ইতিমধ্যে একটি সুন্দর বিস্তারিত উত্তর দিয়েছে, আমি একটি বিকল্প যুক্ত করতে চাই, যেহেতু আমি মনে করি তার বর্ধিত বাস্তবায়ন অত্যধিক জটিল।

আমরা একটি বিমূর্ত বেস ক্লাস দিয়ে শুরু করি, যা সমস্ত বস্তুর ধরণের জন্য ইন্টারফেস সরবরাহ করে:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

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

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

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

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

পরীক্ষার জন্য কিছু কোড যুক্ত করুন:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

সংযোজন (12 জানুয়ারী 2019):

GetClassNameStatic () ফাংশনটি ব্যবহার করার পরিবর্তে, আপনি ক্লাসের নামটি একটি স্থিতিশীল সদস্য এমনকি "ইনলাইন" হিসাবেও সংজ্ঞায়িত করতে পারেন, যা আইআইআরসি সি ++ 11 (যাবতীয় সংশোধনকারীদের দ্বারা ভয় পাবেন না) থেকে কাজ করে:

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

এটা সম্ভব. দুটি ফাংশন করুন: স্থির এবং ভার্চুয়াল

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

4
এছাড়াও, স্থিতিশীল পদ্ধতিগুলি কনস্টের হতে পারে না। এটি ঠিক বোঝায় না, কোন উদাহরণটি তারা পরিবর্তন করতে যাচ্ছেন না?
ডেভিড রদ্রিগেজ - ড্রিবিস

1
এটি বেশিরভাগ ক্ষেত্রে কেবল কোড নকল ation সাবক্লাসের পক্ষে ধারণাটি কেবল স্থিতিশীল সদস্যের প্রয়োজন, এটিতে অ্যাক্সেসের কোড থাকতে হবে না।
einpoklum

8

না, এটি সম্ভব নয়, কারণ স্থির সদস্য ফাংশনে একটি thisপয়েন্টারের অভাব রয়েছে । এবং স্থিতিশীল সদস্য (উভয় ফাংশন এবং ভেরিয়েবল) প্রতি-সেয়েই আসলে শ্রেণীর সদস্য নয়। এগুলি কেবল ClassName::memberশ্রেণীর অ্যাক্সেস স্পেসিফায়ারদের দ্বারা আহ্বান করা এবং মেনে চলার ঘটনা ঘটে । তাদের স্টোরেজ শ্রেণীর বাইরে কোথাও সংজ্ঞায়িত করা হয়; প্রতিবার ক্লাসের কোনও অবজেক্ট ইনস্ট্যান্ট করলে স্টোরেজ তৈরি হয় না। শ্রেণীর সদস্যদের পয়েন্টারগুলি শব্দার্থক এবং সিনট্যাক্সে বিশেষ। স্থিতিশীল সদস্যের একটি পয়েন্টার হ'ল সমস্ত শুভেচ্ছায় একটি সাধারণ পয়েন্টার।

একটি শ্রেণীর ভার্চুয়াল ফাংশনগুলির জন্য thisপয়েন্টারটি প্রয়োজন এবং এটি ক্লাসের সাথে খুব মিলিত হয়, সুতরাং সেগুলি স্থির থাকতে পারে না।


1
শুধুমাত্র অ স্থির ফাংশনগুলির জন্য একটি this পয়েন্টার প্রয়োজন । স্ট্যাটিক ফাংশন কোনও উদাহরণের সাথে সুনির্দিষ্ট নয় এবং এটির প্রয়োজন হবে না। সুতরাং - এটি ভার্চুয়াল স্ট্যাটিক সদস্যদের অসম্ভব বলে কোনও কারণ নয়।
einpoklum

7

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


3

আমি মনে করি আপনি যা করতে চেষ্টা করছেন তা টেমপ্লেটের মাধ্যমে করা যেতে পারে। আমি এখানে লাইন মধ্যে পড়ার চেষ্টা করছি। আপনি যা করার চেষ্টা করছেন তা হ'ল কিছু কোড থেকে একটি পদ্ধতি কল করা যেখানে এটি একটি উদ্ভূত সংস্করণটিকে কল করে কিন্তু কলারটি কোন শ্রেণিটি নির্দিষ্ট করে না। উদাহরণ:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

আপনি বারটিকে নির্দিষ্ট না করে এম এর বার সংস্করণে কল করার চেষ্টা করুন ()। স্ট্যাটিক্সের জন্য আপনি যেভাবে করেন তা হ'ল কোনও টেম্পলেট ব্যবহার করা। সুতরাং এটির মতো পরিবর্তন করুন:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}

1
আপনি যদি আপনার কোড 4 স্পেসে প্রবেশ করেন তবে আপনি এটি স্বয়ংক্রিয়ভাবে ফর্ম্যাট করতে পারেন। বিকল্পভাবে আমি বিশ্বাস করি আপনি একই উদ্দেশ্যটি ইনলাইন অর্জন করতে ব্যাক টিকটি ব্যবহার করতে পারেন।
চোলিদা

1
এটি স্পষ্টই আমি মিস করেছি। ধন্যবাদ. এখনও, পিউবিক সদস্যদের অদ্ভুত হয়।
allesblinkt

এম () স্থির ফাংশন নয়। কীভাবে এটি টি :: এম () বলা হয়?
DDukDDak99

3

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


2
কেবলমাত্র উদাহরণ-নির্দিষ্ট ভার্চুয়াল পদ্ধতিগুলির জন্য উদাহরণগুলির vtable প্রয়োজন। আপনার কাছে একটি স্ট্যাটিক থাকতে পারে - প্রতি ক্লাসে - ভেটেবল। এবং যদি আপনি উদাহরণগুলি জানতে চান তবে কেবল উদাহরণের ভিটিবেল থেকে ক্লাস স্ট্যাটিক্স ভিটিবেলকেও নির্দেশ করুন।
einpoklum

2

এটি সম্ভব নয়, তবে এটি কেবল একটি বাদ দেওয়া কারণ। এটি এমন কিছু নয় যা অনেক লোক দাবি করে বলে "অর্থবোধ করে না"। পরিষ্কার করে বলতে গেলে, আমি এই জাতীয় কিছু নিয়ে কথা বলছি:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

এটি 100% এমন কিছু যা বাস্তবায়িত হতে পারে (এটি ঠিক হয়নি), এবং আমি এমন কিছু যুক্তিযুক্ত যা দরকারী।

সাধারণ ভার্চুয়াল ফাংশন কীভাবে কাজ করে তা বিবেচনা করুন। staticগুলি সরান এবং কিছু অন্যান্য জিনিস যোগ করুন এবং আমাদের আছে:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

এটি সূক্ষ্মভাবে কাজ করে এবং মূলত যা ঘটে তা হ'ল সংকলকটি দুটি টেবিল তৈরি করে, যাকে ভিটিবেলস বলা হয় এবং ভার্চুয়াল ফাংশনগুলিতে সূচকগুলি এই জাতীয় নির্ধারণ করে

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

ভার্চুয়াল ফাংশন সহ প্রতিটি ক্লাসের একটি অন্য ক্ষেত্রের সাথে বাড়ানো হয় যা তার ভিটিবেলের দিকে নির্দেশ করে, তাই সংকলকটি মূলত তাদেরকে এটির মতো করে তোলে:

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

তারপরে ফোন করলে আসলে কী ঘটে b->sayMyName()? মূলত এটি:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(প্রথম প্যারামিটারটি হয়ে যায় this))

ঠিক আছে, সুতরাং এটি স্থির ভার্চুয়াল ফাংশনগুলির সাথে কীভাবে কাজ করবে? আচ্ছা স্থির এবং অ স্থিতিশীল সদস্য ফাংশনগুলির মধ্যে পার্থক্য কী? পার্থক্য হ'ল পরেরটি thisপয়েন্টার পায়।

আমরা স্থির ভার্চুয়াল ফাংশনগুলির সাথে ঠিক একই কাজ করতে পারি - কেবল thisপয়েন্টারটি সরিয়ে ফেলুন ।

b->vtable[Base_Virtual_Functions::sayMyName]();

এটি তখন উভয় বাক্য গঠন সমর্থন করতে পারে:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

সুতরাং সমস্ত nayayers উপেক্ষা করুন। এটা করে না। কেন তখন এটি সমর্থন করা হয় না? আমি মনে করি এটি এর কারণ খুব কম সুবিধা এবং এটি কিছুটা বিভ্রান্তিকর হতে পারে।

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

এর অর্থ এই যে আপনি যখন উদাহরণস্বরূপ উপস্থিত হন এবং ক্ষেত্রে যখন আপনার নজির নেই তখন ক্ষেত্রে পৃথক স্থিতিশীল এবং অ-স্থিতিশীল ফাংশন আপনার নেই, তবে এটি বিভ্রান্তিকর হতে পারে যে আপনি যখন ব্যবহার করবেন তখন এটি কেবল সত্যই "ভার্চুয়াল" উদাহরণ কল।


0

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


0

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

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

ব্যাকগ্রাউন্ডের জন্য দেখুন: http://en.wikedia.org/wiki/Visitor_ Pattern

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

struct SomeObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

তারপরে প্রতিটি কংক্রিট অবজেক্টের জন্য:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

এবং তারপরে বেস দর্শকের সংজ্ঞা দিন:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

তারপরে কংক্রিট ভিজিটর উপযুক্ত স্ট্যাটিক ফাংশন নির্বাচন করে:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

অবশেষে, এটি ব্যবহার করুন:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

মন্তব্য:

  • একটি অনুশীলন হিসাবে দৃ়তা বাকি।
  • আপনি একটি স্ট্যাটিক থেকে একটি রেফারেন্স ফিরে এসেছেন। আপনার যদি সিঙ্গলটন না থাকে তবে এটি প্রশ্নবিদ্ধ।

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

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};

ভার্চুয়াল স্ট্যাটিক পদ্ধতিগুলি যদি এগুলির অস্তিত্ব থাকে তবে কোনও পরিস্থিতিতে কোনও কিছুর উপর নির্ভর করবে না - তবে তাদের অনুরোধ করার জন্য দৃষ্টান্তটির ধরনটি জানতে হবে। এটি একটি সংকলক দ্বারা কাজ করা যেতে পারে (উদাহরণস্বরূপ ভার্চুয়াল স্ট্যাটিক পদ্ধতি এবং সদস্যদের সাথে পয়েন্টার সহ কিছু শ্রেণীর একক ডেটা স্ট্রাকচার ব্যবহার করে)) এটি অবশ্যই পদগুলির সাথে বৈপরীত্য নয়।
einpoklum

এটি পদগুলির মধ্যে একটি বৈপরীত্য বা না তা শব্দার্থবিজ্ঞানের প্রশ্ন। কেউ কল্পনা করতে পারে যে সি ++ উদাহরণ দিয়ে কল স্ট্যাটিক্সকে অনুমতি দেয় (উদাহরণস্বরূপ, Foo foo; ... foo::bar();পরিবর্তে Foo::bar();)। এটি ভিন্ন নয় decltype(foo)::bar();তবে এটি আবার স্থিরভাবে আবদ্ধ হবে। দর্শনার্থী পদ্ধতির কাছে এই আচরণটি পাওয়ার একটি যুক্তিসঙ্গত উপায় বলে মনে হচ্ছে কেবল স্থিতিশীল পদ্ধতিটিকে ভার্চুয়াল কনস্ট পদ্ধতি হিসাবে তৈরি না করে।
বেন

0

সি ++ সহ আপনি crt পদ্ধতিতে স্থিত উত্তরাধিকার ব্যবহার করতে পারেন। উদাহরণস্বরূপ, এটি উইন্ডো টেম্পলেট atl & wtl এ ব্যাপকভাবে ব্যবহৃত হয়।

Https://en.wikedia.org/wiki/rciously_recurring_template_pattern দেখুন

সহজ কথা বলতে গেলে আপনার কাছে এমন একটি ক্লাস রয়েছে যা ক্লাস মাইক্লাসের মতো নিজে থেকেই টেম্প্লেটেড: পাবলিক মায়্যান্সস্টোর। এই জায়গা থেকে myancestor ক্লাস আপনার স্ট্যাটিক টি :: আপনারআইএমপিএল ফাংশন কল করতে পারেন।


-1

হতে পারে আপনি নীচে আমার সমাধান চেষ্টা করতে পারেন:

class Base {
public:
    Base(void);
    virtual ~Base(void);

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}

হ্যাঁ, আমি জানি, 4 বছর। যারা এত বেশি বিবরণে কোডটি পড়তে চান না তাদের জন্য স্কোরটি ব্যাখ্যা করছেন। Base::mSelfকোনও উদ্ভূত শ্রেণীর সর্বাধিক নির্মমভাবে নির্মিত উদাহরণটিকে বোঝায়, এমনকি যদি সেই উদাহরণটি ধ্বংস হয়ে যায় । তাই class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */যা চেয়েছিল তা নয়।
জেসি চিশলম

-3

অন্যরা যেমন বলেছে, এখানে 2 টি গুরুত্বপূর্ণ টুকরো রয়েছে:

  1. thisস্থির ফাংশন কল করার সময় কোনও পয়েন্টার নেই এবং and
  2. thisগঠন যেখানে ভার্চুয়াল টেবিল, বা thunk, সন্ধান করার যা রানটাইম পদ্ধতি কল ব্যবহার করা হয় পয়েন্টার পয়েন্ট।

একটি স্থির ফাংশন সংকলন সময়ে নির্ধারিত হয়।

আমি ক্লাসে সি ++ স্ট্যাটিক সদস্যগুলিতে এই কোড উদাহরণটি দেখিয়েছি ; এটি দেখায় যে নাল পয়েন্টারটি দিয়ে আপনি একটি স্থির পদ্ধতিতে কল করতে পারেন:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}

6
প্রযুক্তিগতভাবে, এটি অনির্ধারিত আচরণ। আপনি কোনও কারণে নাল পয়েন্টারকে সম্মান করতে পারবেন না। নাল পয়েন্টার দিয়ে আপনি যা করতে পারেন তা হ'ল) ​​এটির জন্য অন্য পয়েন্টার নির্ধারণ করুন এবং খ) এটি অন্য পয়েন্টারের সাথে তুলনা করুন।
কিথবি

1
তদ্ব্যতীত, আপনি কেবল এটি সমতার জন্য তুলনা করতে পারেন (বা বৈষম্য_ অন্য পয়েন্টারের সাথে, অর্ডার দিচ্ছেন না। যেমন p < null, p >= nullইত্যাদি সমস্তই
অপরিজ্ঞাত

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