কনস্ট সি ++ ডিআরওয়াই কৌশল


14

তুচ্ছ সি ++ কনট সম্পর্কিত নকল এড়ানোর জন্য, এমন কোনও ঘটনা রয়েছে যেখানে কনস্ট_কাস্ট কাজ করবে তবে একটি বেসরকারী কনস্ট ফাংশন নন-কনস্ট্যান্টকে ফেরত দেবে না?

স্কট মায়ার্স এর কার্যকর সি ++ আইটেম 3 এ তিনি পরামর্শ দিয়েছেন যে একটি স্ট্যাটিক কাস্টের সাথে মিলিত একটি কনস্ট_কাস্ট ডুপ্লিকেট কোড এড়াতে কার্যকর এবং নিরাপদ উপায় হতে পারে, যেমন

const void* Bar::bar(int i) const
{
  ...
  return variableResultingFromNonTrivialDotDotDotCode;
}
void* Bar::bar(int i)
{
  return const_cast<void*>(static_cast<const Bar*>(this)->bar(i));
}

মায়াররা আরও ব্যাখ্যা করে যে কনস্ট্যান্ড ফাংশনটিকে নন-কনস্ট্যান্ট ফাংশন কল করা বিপজ্জনক।

নীচের কোডটি একটি পাল্টা উদাহরণ দেখাচ্ছে:

  • মায়ারদের পরামর্শের বিপরীতে, কখনও কখনও স্থির কাস্টের সাথে মিলিত কনস্ট_কাস্ট বিপজ্জনক হয়
  • কখনও কখনও কনস্টের ফাংশনটি নন-কনস্ট্যান্ট করা কম বিপজ্জনক হয়
  • কখনও কখনও উভয় উপায়ে একটি কনস্টেস্ট ব্যবহার করে সম্ভাব্য কার্যকর সংকলক ত্রুটিগুলি লুকান
  • কোনও কনস্ট্যান্ট কাস্টম এড়ানো এবং অতিরিক্ত কনস্টেটের ব্যক্তিগত সদস্যকে নন-কনস্ট্যান্ট ফিরিয়ে দেওয়া অন্য বিকল্প is

কোড ডুপ্লিকেশন এড়ানোর কনস্ট_কাস্ট কৌশলগুলির মধ্যে কোনটিই ভাল অনুশীলন হিসাবে বিবেচিত হয়? আপনি কি পরিবর্তে ব্যক্তিগত পদ্ধতি কৌশল পছন্দ করবেন? কনস্ট_কাস্ট কাজ করবে এমন কোনও মামলা আছে তবে একটি ব্যক্তিগত পদ্ধতি কাজ করে না? অন্যান্য অনুলিপি আছে (নকল ছাড়াও)?

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

class Foo
{
  public:
    Foo(const LongLived& constLongLived, LongLived& mutableLongLived)
    : mConstLongLived(constLongLived), mMutableLongLived(mutableLongLived)
    {}

    // case A: we shouldn't ever be allowed to return a non-const reference to something we only have a const reference to

    // const_cast prevents a useful compiler error
    const LongLived& GetA1() const { return mConstLongLived; }
    LongLived& GetA1()
    {
      return const_cast<LongLived&>( static_cast<const Foo*>(this)->GetA1() );
    }

    /* gives useful compiler error
    LongLived& GetA2()
    {
      return mConstLongLived; // error: invalid initialization of reference of type 'LongLived&' from expression of type 'const LongLived'
    }
    const LongLived& GetA2() const { return const_cast<Foo*>(this)->GetA2(); }
    */

    // case B: imagine we are using the convention that const means thread-safe, and we would prefer to re-calculate than lock the cache, then GetB0 might be correct:

    int GetB0(int i) { return mCache.Nth(i); }
    int GetB0(int i) const { return Fibonachi().Nth(i); }

    /* gives useful compiler error
    int GetB1(int i) const { return mCache.Nth(i); } // error: passing 'const Fibonachi' as 'this' argument of 'int Fibonachi::Nth(int)' discards qualifiers
    int GetB1(int i)
    {
      return static_cast<const Foo*>(this)->GetB1(i);
    }*/

    // const_cast prevents a useful compiler error
    int GetB2(int i) { return mCache.Nth(i); }
    int GetB2(int i) const { return const_cast<Foo*>(this)->GetB2(i); }

    // case C: calling a private const member that returns non-const seems like generally the way to go

    LongLived& GetC1() { return GetC1Private(); }
    const LongLived& GetC1() const { return GetC1Private(); }

  private:
    LongLived& GetC1Private() const { /* pretend a bunch of lines of code instead of just returning a single variable*/ return mMutableLongLived; }

    const LongLived& mConstLongLived;
    LongLived& mMutableLongLived;
    Fibonachi mCache;
};

class Fibonachi
{ 
    public:
      Fibonachi()
      {
        mCache.push_back(0);
        mCache.push_back(1);
      }

      int Nth(int n) 
      {
        for (int i=mCache.size(); i <= n; ++i)
        {
            mCache.push_back(mCache[i-1] + mCache[i-2]);
        }
        return mCache[n];
      }

      int Nth(int n) const
      {
          return n < mCache.size() ? mCache[n] : -1;
      }
    private:
      std::vector<int> mCache;
};

class LongLived {};

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

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

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

উত্তর:


8

কনস্ট্যান্ট এবং নন-কনস্ট্যান্ড সদস্য ফাংশনগুলি বাস্তবায়নের সময় কেবল ফিরে আসা পিটিআর / রেফারেন্স কনস্ট্যান্ট কিনা তার দ্বারা পৃথক হয়, সেরা ডিআরওয়াই কৌশলটি হ'ল:

  1. যদি কোনও অ্যাকসেসর লিখতে থাকেন তবে আপনার সত্যিকারের অ্যাক্সেসরের দরকার আছে কিনা তা বিবেচনা করুন, স্যামাস্টারের উত্তর এবং http://c2.com/cgi/wiki?AccessorsAreEvil দেখুন
  2. কোডটি যদি তুচ্ছ হয় তবে এটির সদৃশ করুন (উদাহরণস্বরূপ কেবল একজন সদস্যকে ফিরিয়ে দেওয়া)
  3. কনস্টের সাথে সম্পর্কিত সদৃশতা এড়াতে কোনও কনস্টাস্টকাস্ট ব্যবহার করবেন না
  4. তুচ্ছ তাত্পর্যপূর্ণ প্রতিলিপি এড়াতে, কনস্ট্যান্ড এবং অ-কনস্ট্যান্ট উভয় পাবলিক ফাংশন কল করে এমন একটি কনস্ট্যান্ট ফিরিয়ে একটি প্রাইভেট কনস্ট ফাংশন ব্যবহার করুন

যেমন

public:
  LongLived& GetC1() { return GetC1Private(); }
  const LongLived& GetC1() const { return GetC1Private(); }
private:
  LongLived& GetC1Private() const { /* non-trivial DRY logic here */ }

আসুন একে বেসরকারী কনস্টের ফাংশনটিকে নন-কনস্ট্যান্ট প্যাটার্নটি বলি ।

সংকলকটি এখনও সম্ভাব্য উপকারী চেকগুলি সম্পাদন করতে এবং কনস্টের সম্পর্কিত ত্রুটি বার্তাগুলির প্রতিবেদন করার অনুমতি দেওয়ার সময় সরাসরি স্ট্র্যান্ড ফ্যাশনে নকলগুলি এড়াতে এটি সেরা কৌশল strategy


আপনার যুক্তিগুলি বরং দৃinc়প্রত্যয়ী , তবে আমি বরং আশ্চর্য হয়েছি যে আপনি কীভাবে কোনও constউদাহরণের থেকে অবিচ্ছিন্ন রেফারেন্স পেতে পারেন (যদি না রেফারেন্স ঘোষিত mutableকোনও বিষয় না হয় , বা যদি আপনি const_castউভয় ক্ষেত্রে নিয়োগ না করেন তবে উভয় ক্ষেত্রেই প্রব্লেম শুরু হয় না) )। এছাড়াও আমি "প্রাইভেট কনস্ট ফাংশনটি নন-কনস্ট প্যাটার্ন ফিরিয়ে আনতে" খুঁজে পাইনি (যদি এটি প্যাটার্নটি বলার জন্য একটি রসিকতা হিসাবে চিহ্নিত করা হত .... এটি মজাদার নয়;)
idclev 463035818

1
এখানে কোডের ভিত্তিতে একটি সংকলনের উদাহরণ দেওয়া আছে: آئيডোন / ডাব্লুবিই 1 জিবি । দুঃখিত, আমি এটি একটি রসিকতা হিসাবে বোঝাতে চাইনি, তবে এখানে এটি একটি নাম দেওয়ার অর্থ ছিল (অসম্ভব ঘটনায় এটির নামটি প্রযোজ্য), এবং আমি আরও উত্তর দেওয়ার জন্য উত্তরটিতে শব্দটি আপডেট করেছি। আমি এটি লেখার কয়েক বছর হয়ে গেছে, এবং কেন আমি মনে করি না কেন আমি কনস্ট্রাক্টরের একটি রেফারেন্স পাস করার একটি উদাহরণ প্রাসঙ্গিক বলে মনে করি।
জেডিম্যাটটিও

উদাহরণের জন্য ধন্যবাদ, আমার এখন সময় নেই, তবে আমি অবশ্যই এটিতে ফিরে আসব। Fwiw এখানে একটি উত্তর যে উপহার একই পদ্ধতির এবং মন্তব্য অনুরূপ বিষয় উল্লেখ করা হয়েছে: stackoverflow.com/a/124209/4117728
idclev 463035818

1

হ্যাঁ, আপনি ঠিক বলেছেন: বহু সি ++ প্রোগ্রাম যে কনস্ট্যান্ট-শোধনের চেষ্টা করে তা ডিআরওয়াই নীতিটির লঙ্ঘন এবং এমনকি বেসরকারী সদস্য নন-কনস্ট্যান্ট ফিরে আসাও স্বাচ্ছন্দ্যের জন্য কিছুটা জটিল।

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

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


এটি অ্যাক্সেসরগুলি ভাল কিনা তা বিতর্কের পক্ষে মনে হয়, উদাহরণস্বরূপ c2.com/cgi/wiki?AccessorsAreEvilআলোচনাটি দেখুন । অনুশীলনে, আপনি অ্যাক্সেসরদের সম্পর্কে যা ভাবেন তা বিবেচনা না করেই, বড় কোড বেসগুলি প্রায়শই সেগুলি ব্যবহার করে এবং যদি তারা সেগুলি ব্যবহার করে তবে ডিআরওয়াই নীতিটি মেনে চলা ভাল। সুতরাং আমি মনে করি যে প্রশ্নটি জিজ্ঞাসা করা উচিত নয় তার চেয়ে বেশি উত্তর পাওয়ার যোগ্য।
JDiMatteo

1
এটি অবশ্যই জিজ্ঞাসা করার মতো একটি প্রশ্ন :-) এবং আমি এমনকি অস্বীকার করব না যে আপনার সময়ে সময়ে অ্যাক্সেসর প্রয়োজন need আমি কেবল বলছি যে প্রোগ্রামিং শৈলী যা অ্যাক্সেসরগুলির উপর ভিত্তি করে নয় তা সমস্যাটিকে অনেকাংশে হ্রাস করে। এটি সামগ্রিকভাবে সমস্যার সমাধান করে না, তবে এটি আমার পক্ষে কমপক্ষে যথেষ্ট ভাল।
মাস্টার -
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.