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


219

কোডটি বিবেচনা করুন:

#include <stdio.h>

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){
    Derived obj;
    obj.gogo(7);
}

এই ত্রুটিটি পেয়েছেন:

> জি ++ -অপেনডেন্টিক -ও পরীক্ষা.পি.পি.-পরীক্ষা
test.cpp: ফাংশনে main int প্রধান () ':
test.cpp: 31: ত্রুটি: `ডারাইভড :: গোগো (ইনট) 'তে কল করার জন্য কোনও মিল নেই
test.cpp: 21: নোট: পরীক্ষার্থীরা হ'ল: ভার্চুয়াল শূন্যতা ডেরাইভড :: গোগো (ইনট *) 
test.cpp: 33: 2: সতর্কতা: ফাইলের শেষে কোনও নতুন লাইন নেই
> প্রস্থান কোড: 1

এখানে, ডেরিভড শ্রেণীর ফাংশনটি বেস শ্রেণিতে একই নামের (স্বাক্ষর নয়) সমস্ত ফাংশন গ্রহ করছে। একরকম, সি ++ এর এই আচরণটি ঠিক দেখাচ্ছে না। পলিমারফিক নয়।



8
উজ্জ্বল প্রশ্ন, আমি কেবল সম্প্রতি এটি আবিষ্কার করেছি
ম্যাট জেন্ডার

11
আমি মনে করি বর্জন (ম্যাক পোস্ট করা লিংক থেকে) এটিকে একটি বাক্যে সেরা বলেছিল: "সি ++ তে, স্কোপগুলি জুড়ে কোনও ওভারলোডিং নেই - উত্পন্ন শ্রেণীর স্কোপগুলি এই সাধারণ নিয়মের ব্যতিক্রম নয়" "
শিববধু

7
@ আশিসের সেই লিঙ্কটি নষ্ট হয়ে গেছে। এখানে সঠিকটি (এখনকার হিসাবে) - stroustrup.com/bs_faq2.html#overloadderivated
nsane

3
এছাড়াও, এটি চিহ্নিত করতে চেয়েছিলেন যে obj.Base::gogo(7);লুকানো ফাংশনটি কল করে এখনও কাজ করে।
ফোরামুলেটর

উত্তর:


406

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

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

উদাহরণস্বরূপ, ধরা যাক বেস ক্লাসে Bএকটি সদস্য ফাংশন থাকে fooযা টাইপের একটি প্যারামিটার নেয় void *এবং সমস্ত কলগুলি foo(NULL)সমাধান করা হয় B::foo(void *)। আসুন বলে রাখুন যে কোনও নাম গোপন নেই এবং এটি B::foo(void *)থেকে নেমে আসা বিভিন্ন শ্রেণিতে এটি দৃশ্যমান B। তবে, আসুন আমরা Dক্লাসের কিছু [পরোক্ষ, দূরবর্তী] বংশধরকে Bএকটি ফাংশন foo(int)সংজ্ঞায়িত করা হয়। এখন, নাম গোপন ছাড়া Dউভয় হয়েছে foo(void *)এবং foo(int)দৃশ্যমান এবং জমিদার রেজল্যুশন অংশগ্রহণ। কোন কলগুলি কী foo(NULL)ধরণের কোনও অবজেক্টের মাধ্যমে তৈরি করা হলে তা সমাধান করবেD ? তারা সমাধান করবে D::foo(int), যেহেতু intঅবিচ্ছেদ্য শূন্যের জন্য আরও ভাল ম্যাচ (যেমন ieNULL) যে কোনও পয়েন্টার প্রকারের চেয়ে। সুতরাং, ক্রিয়াকলাপ জুড়ে foo(NULL)একটি ক্রিয়াকলাপের সমাধানের জন্য আহ্বান জানানো হয় , যখন D(এবং এর নীচে) তারা হঠাৎ অন্যটির কাছে সমাধান করে।

আর একটি উদাহরণ দেওয়া হয়েছে ডিজিটাল অ্যান্ড ইভোলিউশন এর সি ++ এর পৃষ্ঠা page 77:

class Base {
    int x;
public:
    virtual void copy(Base* p) { x = p-> x; }
};

class Derived{
    int xx;
public:
    virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};

void f(Base a, Derived b)
{
    a.copy(&b); // ok: copy Base part of b
    b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}

এই নিয়ম ব্যতীত খ এর রাজ্য আংশিকভাবে আপডেট করা হবে, ফলে টুকরো টুকরো হয়ে যাবে।

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

আপনি যেমনটি আপনার মূল পোস্টে সঠিকভাবে পর্যবেক্ষণ করেছেন (আমি "পলিমর্ফিক নয়" মন্তব্যটি উল্লেখ করছি), এই আচরণটি ক্লাসগুলির মধ্যে আইএস-এ সম্পর্কের লঙ্ঘন হিসাবে দেখা যেতে পারে। এটি সত্য, তবে আপাতদৃষ্টিতে ফিরে এসে সিদ্ধান্ত নেওয়া হয়েছিল যে শেষ নামটি গোপন করা একটি কম মন্দ হিসাবে প্রমাণিত হবে।


22
হ্যাঁ, এটি প্রশ্নের সত্যই উত্তর। ধন্যবাদ. আমিও কৌতুহলী ছিলাম।
সর্বময়ী

4
দুর্দান্ত উত্তর! এছাড়াও, ব্যবহারিক বিষয় হিসাবে, সংকলনটি সম্ভবত খুব ধীরে ধীরে ধীরে ধীরে নামতে পারে যদি প্রতিবারের মতো নাম অনুসন্ধানের শীর্ষে যেতে হত।
ড্রয় হল

6
(পুরানো উত্তর, আমি জানি।) এখন nullptrআমি "আপনি যদি void*সংস্করণটি কল করতে চান , আপনার পয়েন্টার টাইপ ব্যবহার করা উচিত " এই বলে আপনার উদাহরণটিতে আপত্তি জানাব । এটির আরও খারাপ উদাহরণ থাকতে পারে?
GManNickG

3
নাম গোপন করা সত্যই খারাপ নয়। "Is-a" সম্পর্কটি এখনও আছে, এবং বেস ইন্টারফেসের মাধ্যমে উপলব্ধ। তাই হয়তো d->foo()আপনি পাবেন না "IS-একটি Base", কিন্তু static_cast<Base*>(d)->foo() হবে গতিশীল প্রেরণ সহ।
কেরেক এসবি

12
এই উত্তরটি অপ্রত্যাশিত কারণ প্রদত্ত উদাহরণটি গোপনীয়তা বা গোপন ছাড়াই একই আচরণ করে: D :: foo (int) হয় বলা হয় এটি একটি ভাল ম্যাচ কারণ এটিতে এটি বি: ফু (অন্তর্নিহিত) লুকিয়ে রয়েছে।
রিচার্ড ওল্ফ

46

নামটির সমাধানের নিয়মগুলি বলছে যে নামের অনুসন্ধান প্রথম স্কোপে থামবে যেখানে কোনও মিলের নাম পাওয়া যায়। সেই সময়ে, ওভারলোড রেজোলিউশন নিয়মগুলি উপলভ্য ফাংশনগুলির সর্বোত্তম মিলটি খুঁজে পেতে লিক করে।

এই ক্ষেত্রে, gogo(int*)উদ্ভূত বর্গ সুযোগ (একা) পাওয়া যায়, এবং সেখানে int- * থেকে int- এ থেকে কোন মান রূপান্তর আছে, লুকআপ ব্যর্থ।

সমাধানটি হ'ল ডেরাইভড শ্রেণিতে ব্যবহারের ঘোষণার মাধ্যমে বেস ঘোষণাগুলি আনা:

using Base::gogo;

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


10
ওপি: "উত্পন্ন শ্রেণিতে একটি ওভাররাইড করা ফাংশন বেস শ্রেণীর অন্যান্য ওভারলোডগুলি কেন আড়াল করে?" এই উত্তর: "কারণ এটি করে"।
রিচার্ড উলফ

12

এটি "বাই ডিজাইন"। এই ধরণের পদ্ধতির জন্য সি ++ ওভারলোড রেজোলিউশন নীচের মত কাজ করে।

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

যেহেতু ডেরিভডের "গোগো" নামে কোনও মিলের ফাংশন নেই, তাই ওভারলোড রেজোলিউশন ব্যর্থ।


2

নাম গোপন করা অর্থপূর্ণ কারণ এটি নাম রেজোলিউশনে অস্পষ্টতাকে বাধা দেয়।

এই কোডটি বিবেচনা করুন:

class Base
{
public:
    void func (float x) { ... }
}

class Derived: public Base
{
public:
    void func (double x) { ... }
}

Derived dobj;

যদি ডারাইভড এ Base::func(float)লুকায়িত না থাকে Derived::func(double)তবে কল করার সময় আমরা বেস ক্লাস ফাংশনটি কল করতাম dobj.func(0.f), যদিও একটি ফ্লোটকে ডাবল হিসাবে উন্নীত করা যায়।

তথ্যসূত্র: http://bastian.rieck.ru/blog/posts/2016/name_hide_cxx/

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