ভার্চুয়াল ফাংশনগুলিতে কি ডিফল্ট পরামিতি থাকতে পারে?


164

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

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


1
এটি পরীক্ষা করা সহজ জিনিস বলে মনে হচ্ছে। আপনি কি এটা চেষ্টা করেছেন?

22
আমি এটি চেষ্টা করার প্রক্রিয়াতে রয়েছি তবে আচরণটি কীভাবে "সংজ্ঞায়িত" হবে তার কোনও ठोस তথ্য আমি পাইনি তাই শেষ পর্যন্ত আমি আমার নির্দিষ্ট সংকলকের জন্য একটি উত্তর খুঁজে পাব তবে এটি আমাকে বলবে না যে সমস্ত সংকলক একই কাজ করবে কিনা that জিনিস। আমি প্রস্তাবিত অনুশীলনে আগ্রহী।
আর্নল্ড স্পেন্স

1
আচরণটি সুস্পষ্টভাবে সংজ্ঞায়িত করা হয়েছে এবং আমি সন্দেহ করি যে আপনি একটি সংকলকটি পেয়েছেন যা এটির ভুল হয়ে গেছে (ভাল, আপনি যদি সিসি 1.x, বা ভিসি ++ 1.0 বা এরকম কিছু পরীক্ষা করেন)। প্রস্তাবিত অনুশীলন এটি করার বিরুদ্ধে।
জেরি কফিন

উত্তর:


212

ভার্চুয়ালগুলির ডিফল্ট থাকতে পারে। বেস শ্রেণিতে খেলাপি ক্লাসগুলি ডিফল্টগুলি উত্তরাধিকার সূত্রে প্রাপ্ত হয় না।

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

কিছু সংকলক আলাদা কিছু করতে পারে তবে সি ++ 03 এবং সি ++ 11 স্ট্যান্ডার্ড এটাই বলে:

8.3.6.10:

ভার্চুয়াল ফাংশন কল (10.3) পয়েন্টার বা রেফারেন্সের স্থির প্রকারের দ্বারা নির্ধারিত ভার্চুয়াল ফাংশনের ঘোষণায় ডিফল্ট আর্গুমেন্টগুলি ব্যবহার করে বা অবজেক্টটি নির্দেশ করে reference একটি উত্পন্ন শ্রেণিতে একটি ওভাররাইডিং ফাংশন ওভাররাইড করা ফাংশন থেকে ডিফল্ট আর্গুমেন্টগুলি অর্জন করে না। উদাহরণ:

struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m()
{
  B* pb = new B;
  A* pa = pb;
  pa->f(); //OK, calls pa->B::f(7)
  pb->f(); //error: wrong number of arguments for B::f()
}

ডিফল্ট কী উঠেছে তা প্রদর্শন করার জন্য এখানে একটি নমুনা প্রোগ্রাম is আমি কেবল ব্রিভটির জন্য এস এর structচেয়ে এখানে ব্যবহার করছি class- classএবং structপ্রায় সব ক্ষেত্রেই ডিফল্ট দৃশ্যমানতা বাদে ঠিক একই রকম।

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

using std::stringstream;
using std::string;
using std::cout;
using std::endl;

struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };

string Base::Speak(int n) 
{ 
    stringstream ss;
    ss << "Base " << n;
    return ss.str();
}

string Der::Speak(int n)
{
    stringstream ss;
    ss << "Der " << n;
    return ss.str();
}

int main()
{
    Base b1;
    Der d1;

    Base *pb1 = &b1, *pb2 = &d1;
    Der *pd1 = &d1;
    cout << pb1->Speak() << "\n"    // Base 42
        << pb2->Speak() << "\n"     // Der 42
        << pd1->Speak() << "\n"     // Der 84
        << endl;
}

এই প্রোগ্রামটির আউটপুট (এমএসভিসি 10 এবং জিসিসি 4.4 এ):

Base 42
Der 42
Der 84

রেফারেন্সের জন্য ধন্যবাদ, এটি আমাকে যে আচরণটি আমি কম্পাইলারগুলিতে যুক্তিসঙ্গতভাবে আশা করতে পারি তা বলে (আশা করি)।
আর্নল্ড স্পেন্স

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

আমি জিসিসি ৪.৮.১ ব্যবহার করছি এবং আমি সংকলনের ত্রুটিটি "যুক্তিগুলির ভুল সংখ্যা" পাই না !!! বাগটি খুঁজে পেতে আমাকে দেড় দিন সময় নিয়েছে ...
স্টেফেন

2
তবে এর কোনও কারণ আছে কি? এটি স্থির ধরণের দ্বারা নির্ধারিত হয় কেন?
ব্যবহারকারী 1289

2
ক্ল্যাং-পরিপাটি ভার্চুয়াল পদ্ধতিতে ডিফল্ট পরামিতিগুলিকে অযাচিত কিছু হিসাবে বিবেচনা করে এবং সে সম্পর্কে একটি সতর্কতা জারি করে: github.com/llvm-mirror/clang-tools-extra/blob/master/clang-tidy/…
মার্টিন

38

এটি ছিল হার্বস সাটারের সপ্তাহের পোস্টগুলির প্রথম দিকের গুরুদের বিষয়

বিষয়টিতে তিনি প্রথম যে কথাটি বলছেন তা হ'ল না।

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

প্রদত্ত

class A {
    virtual void foo(int i = 1) { cout << "A::foo" << i << endl; }
};
class B: public A {
    virtual void foo(int i = 2) { cout << "B::foo" << i << endl; }
};
void test() {
A a;
B b;
A* ap = &b;
a.foo();
b.foo();
ap->foo();
}

আপনার A :: foo1 বি :: foo2 বি :: foo1 পাওয়া উচিত


7
ধন্যবাদ। হার্ব সাটারের একটি "এটি করবেন না" কিছুটা ওজন বহন করে।
আর্নল্ড স্পেন্স

2
@ আর্নল্ডস্পেন্স, আসলে হার্ব সটার এই সুপারিশের বাইরে। তিনি বিশ্বাস করেন যে কোনও ইন্টারফেসে ভার্চুয়াল পদ্ধতিগুলি মোটেই থাকা উচিত নয়: getw.ca/publications/mill18.htm । একবার আপনার পদ্ধতিগুলি কংক্রিট হয়ে যায় এবং (হওয়া উচিত নয়) ওভাররাইড করা যায় না, তাদের ডিফল্ট পরামিতিগুলি দেওয়া নিরাপদ।
মার্ক রান্সম

1
আমি বিশ্বাস করি তিনি কি দ্বারা বোঝানো "না যে " ছিল পদ্ধতি অগ্রাহ্য মধ্যে "ডিফল্ট পরামিতির ডিফল্ট মান পরিবর্তন করবেন না", না "ভার্চুয়াল পদ্ধতিতে ডিফল্ট পরামিতি উল্লেখ না"
Weipeng এল

6

এটি একটি খারাপ ধারণা, কারণ আপনি যে ডিফল্ট আর্গুমেন্টগুলি পান সেটি নির্ভর করবে বস্তুর স্থিতিশীল প্রকারের উপর , তবে virtualপ্রেরণ করা ফাংশনটি গতিশীল ধরণের উপর নির্ভর করবে ।

এর অর্থ হ'ল আপনি যখন ডিফল্ট আর্গুমেন্ট সহ কোনও ফাংশনটি কল করেন তখন ডিফল্ট আর্গুমেন্টগুলি সংকলন সময়ে ফাংশনটি হয় কিনা তা নির্বিশেষে পরিবর্তিত হয় virtual

@cppcoder তার [বন্ধ] প্রশ্নে নিম্নলিখিত উদাহরণটি পেশ করেছেন :

struct A {
    virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; }
};
struct B : public A {
    virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; }
};

int main()
{
    A * a = new B();
    a->display();

    A* aa = new A();
    aa->display();

    B* bb = new B();
    bb->display();
}

যা নিম্নলিখিত আউটপুট উত্পাদন করে:

Derived::5
Base::5
Derived::9

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

    A * a = new B();
    a->display(5);

    A* aa = new A();
    aa->display(5);

    B* bb = new B();
    bb->display(9);

4

আপনি অন্যান্য উত্তরগুলি থেকে দেখতে পারেন এটি একটি জটিল বিষয়। এটি করার চেষ্টা বা এটি কী করে তা বোঝার পরিবর্তে (যদি আপনাকে এখনই জিজ্ঞাসা করতে হয় তবে রক্ষণকারীকে এখন থেকে এক বছর এটি জিজ্ঞাসা করতে হবে বা দেখতে হবে)।

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


1
এটা মোটেই জটিল নয়। নাম রেজোলিউশন সহ ডিফল্ট পরামিতিগুলি সন্ধান করা হয়। তারা একই নিয়ম অনুসরণ করে।
এডওয়ার্ড স্ট্রেঞ্জ

4

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

#include <iostream>

struct base { 
    virtual void x(int a=0) { std::cout << a; }
    virtual ~base() {}
};

struct derived1 : base { 
    void x(int a) { std:: cout << a; }
};

struct derived2 : base { 
    void x(int a = 1) { std::cout << a; }
};

int main() { 
    base *b[3];
    b[0] = new base;
    b[1] = new derived1;
    b[2] = new derived2;

    for (int i=0; i<3; i++) {
        b[i]->x();
        delete b[i];
    }

    derived1 d;
    // d.x();       // won't compile.
    derived2 d2;
    d2.x();
    return 0;
}

4
@ জিএমান: [সাবধানে নির্দোষের দিকে তাকিয়ে] কী ফুটো? :-)
জেরি কফিন

আমি মনে করি তিনি ভার্চুয়াল ডেস্ট্রাক্টরের অভাবের কথা উল্লেখ করছেন। তবে এক্ষেত্রে এটি ফুটো হবে না।
জন ডিবলিং

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

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

@ ছাপ্পার: কোডটি মূলত কোনও কিছুই মুছেনি। যদিও এটি হাতে থাকা প্রশ্নটির সাথে বেশিরভাগই অপ্রাসঙ্গিক, আমি বেস ক্লাসে একটি ভার্চুয়াল ডটরও যুক্ত করেছি - একটি তুচ্ছ ড্টর সহ এটি খুব কমই গুরুত্বপূর্ণ হয়, তবে জিমন পুরোপুরি সঠিক যে এটি ছাড়াই কোডটি ইউবি রয়েছে।
জেরি কফিন

4

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

সুতরাং পরিবর্তে,

//bad idea
virtual method1(int x = 0, int y = 0, int z = 0)

এটা কর,

//good idea
struct Param1 {
  int x = 0, y = 0, z = 0;
};
virtual method1(const Param1& p)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.