কেন কেউ সি ++ এ নেস্টেড ক্লাস ব্যবহার করবে?


188

কেউ দয়া করে নেস্টেড ক্লাসগুলি বোঝার জন্য এবং ব্যবহার করার জন্য আমাকে কিছু সুন্দর উত্সের দিকে নির্দেশ করতে পারেন? আমার কাছে প্রোগ্রামিং নীতিগুলির মতো কিছু উপাদান রয়েছে এবং এই আইবিএম নলেজ সেন্টার - নেস্টেড ক্লাসগুলির মতো জিনিস

তবে আমি এখনও তাদের উদ্দেশ্য বুঝতে সমস্যা করছি। কেউ আমাকে সাহায্য করতে পারেন?


15
সি ++ এ নেস্টেড ক্লাসগুলির জন্য আমার পরামর্শ কেবল নেস্টেড ক্লাসগুলি ব্যবহার না করা।
বিলি ওনিল

7
তারা ঠিক নিয়মিত ক্লাসের মতো ... নেস্টেড বাদে। কোনও শ্রেণীর অভ্যন্তরীণ বাস্তবায়ন এত জটিল হলে এগুলি ব্যবহার করুন যখন বেশ কয়েকটি ছোট শ্রেণীর দ্বারা এটি খুব সহজেই মডেল করা যায়।
মেগার

12
@ বিলি: কেন? আমার কাছে অত্যধিক বিস্তৃত মনে হচ্ছে।
জন ডিবলিং

30
নেস্টেড ক্লাসগুলি কেন তাদের স্বভাবের কারণে খারাপ, এমন কোনও তর্ক আমি এখনও দেখিনি।
জন ডিবলিং

7
@ 7vies: 1. কারণ এটি কেবল প্রয়োজনীয় নয় - আপনি বাহ্যিকভাবে সংজ্ঞায়িত শ্রেণীর সাথেও একই কাজটি করতে পারেন, যা কোনও প্রদত্ত ভেরিয়েবলের সুযোগকে হ্রাস করে, যা একটি ভাল জিনিস। ২. কারণ আপনি নেস্টেড ক্লাসগুলি যা করতে পারেন তা করতে পারেন typedef। ৩. কারণ তারা এমন একটি পরিবেশে অতিরিক্ত স্তরের ইন্ডেন্টেশন যুক্ত করে যেখানে লম্বা রেখাগুলি এড়ানো ইতিমধ্যে কঠিন 4.. কারণ আপনি একক classঘোষণায় দুটি ধারণাগতভাবে পৃথক পৃথক বস্তু ঘোষণা করছেন ইত্যাদি
বিলি ওনিল

উত্তর:


228

নেস্টেড ক্লাসগুলি প্রয়োগের বিবরণ গোপন করার জন্য দুর্দান্ত cool

তালিকা:

class List
{
    public:
        List(): head(nullptr), tail(nullptr) {}
    private:
        class Node
        {
              public:
                  int   data;
                  Node* next;
                  Node* prev;
        };
    private:
        Node*     head;
        Node*     tail;
};

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

দেখুন std::listবা std::mapতাদের সবগুলিতে লুকানো ক্লাস রয়েছে (বা তারা?)। মুল বক্তব্যটি তারা হতে পারে বা নাও পারে তবে বাস্তবায়নটি বেসরকারী এবং লুকিয়ে থাকার কারণে এসটিএলটির নির্মাতারা কোডটি কীভাবে ব্যবহার করেছিলেন তা প্রভাবিত না করে বা এসটিএলটির চারপাশে প্রচুর পুরানো ব্যাগ রেখে দেওয়ার কারণে কোড আপডেট করতে সক্ষম হয়েছিল they পিছন কিছু বোকা যারা সিদ্ধান্ত নিয়েছে তারা নোড বর্গ যে ভিতরে লুকানো ছিল ব্যবহার করতে চেয়েছিলেন সাথে সামঞ্জস্যের বজায় রাখার জন্য list


9
আপনি যদি এটি করছেন তবে Nodeশিরোনাম ফাইলে একেবারেই প্রকাশ করা উচিত নয়।
বিলি ওনিল

6
@ বিলি ওনিল: আমি যদি এসটিএল বা উত্সাহের মতো একটি শিরোনাম ফাইল বাস্তবায়ন করছি What
মার্টিন ইয়র্ক

5
@ বিলি ওনিল: না এটি মতামত না দিয়ে ভাল ডিজাইনের বিষয়। এটিকে একটি নেমস্পেসে রেখে দেওয়া ব্যবহার থেকে রক্ষা করে না। এটি এখন সর্বজনীন API এর অংশ যা স্থায়ীত্ব বজায় রাখা দরকার।
মার্টিন ইয়র্ক

21
@ বিলি ওনিল: এটি দুর্ঘটনাজনিত ব্যবহার থেকে রক্ষা করে। এটি ব্যক্তিগত যেহেতু এটি ব্যবহার করে এবং এটি ব্যবহার করা উচিত নয় (আপনি বোকামি কিছু না করলে ব্যবহার করা যাবে না) এটিও নথিভুক্ত করে। সুতরাং আপনার এটি সমর্থন করার দরকার নেই। এটিকে একটি নেমস্পেসে রাখার ফলে এটি সর্বজনীন API এর অংশ হয়ে যায় (এমন কিছু যা আপনি এই কথোপকথনে হারিয়ে ফেলেন Public পাবলিক এপিআই এর অর্থ আপনার এটি সমর্থন করা দরকার)।
মার্টিন ইয়র্ক

10
@ বিলি ওনিল: নেস্টেড ক্লাসটির নেস্টেড নেমস্পেসের কিছুটা সুবিধা রয়েছে: আপনি কোনও নেমস্পেসের উদাহরণ তৈরি করতে পারবেন না, তবে আপনি কোনও ক্লাসের উদাহরণ তৈরি করতে পারবেন। detailকনভেনশন হিসাবে : এই ধরণের সম্মেলনের উপর নির্ভর করে নিজের নিজের মনে রাখা উচিত, সংকলকটির উপর নির্ভর করা ভাল যা আপনার জন্য তাদের নজর রাখে।
সাসকিউ

142

নেস্টেড ক্লাসগুলি ঠিক নিয়মিত ক্লাসের মতো তবে:

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

কিছু উদাহরণ:

এটিকে প্রাসঙ্গিক শ্রেণির স্কোপে রাখার জন্য সর্বজনীনভাবে বাসা বাঁধছে


ধরে নিন আপনার কাছে এমন একটি শ্রেণি রয়েছে SomeSpecificCollectionযা শ্রেণীর সামগ্রিক অবজেক্টগুলি করতে চায় Element। এরপরে আপনিও পারেন:

  1. দুটি ক্লাস ঘোষণা করুন: SomeSpecificCollectionএবং Element- খারাপ, কারণ "এলিমেন্ট" নামটি সাধারণত একটি সাধারণ নাম সংঘর্ষের কারণ হিসাবে যথেষ্ট সাধারণ

  2. একটি নেমস্পেস চালু someSpecificCollectionএবং ক্লাস ঘোষণা someSpecificCollection::Collectionএবং someSpecificCollection::Element। নামের সংঘর্ষের ঝুঁকি নেই, তবে এটি কি আর কোনও ভার্বোস পেতে পারে?

  3. দুটি বৈশ্বিক ক্লাস ঘোষণা করুন SomeSpecificCollectionএবং SomeSpecificCollectionElement- এতে সামান্য ত্রুটি রয়েছে, তবে সম্ভবত ঠিক আছে।

  4. গ্লোবাল ক্লাস SomeSpecificCollectionএবং ক্লাসকে Elementতার নেস্টেড ক্লাস হিসাবে ঘোষণা করুন । তারপর:

    • এলিমেন্ট বিশ্বব্যাপী নেমস্পেসে না থাকায় আপনি কোনও নাম সংঘর্ষের ঝুঁকি নেবেন না,
    • বাস্তবায়নের ক্ষেত্রে SomeSpecificCollectionআপনাকে ন্যায়সঙ্গত Elementএবং অন্য যে কোনও জায়গায় উল্লেখ করা হয় SomeSpecificCollection::Element- যা দেখতে + - 3 হিসাবে একই, তবে আরও পরিষ্কার
    • এটি সহজ সরল হয়ে উঠল যে এটি "নির্দিষ্ট সংগ্রহের একটি উপাদান", "সংগ্রহের একটি নির্দিষ্ট উপাদান" নয়
    • এটি দৃশ্যমান যে SomeSpecificCollectionএটিও একটি বর্গ।

আমার মতে, শেষ রূপটি অবশ্যই সবচেয়ে স্বজ্ঞাত এবং তাই সেরা নকশা।

আমাকে চাপ দিন - আরও ভার্বোজের নাম দিয়ে দুটি বৈশ্বিক ক্লাস করা থেকে এটি কোনও বড় পার্থক্য নয়। এটি কেবলমাত্র একটি ছোট্ট বিশদ বিবরণ, তবে imho এটি কোডটিকে আরও স্পষ্ট করে তোলে।

শ্রেণীর ক্ষেত্রের মধ্যে অন্য একটি সুযোগের পরিচয় দেওয়া


এটি টাইপডেফস বা এনামগুলি প্রবর্তনের জন্য বিশেষভাবে কার্যকর। আমি এখানে একটি কোড উদাহরণ পোস্ট করব:

class Product {
public:
    enum ProductType {
        FANCY, AWESOME, USEFUL
    };
    enum ProductBoxType {
        BOX, BAG, CRATE
    };
    Product(ProductType t, ProductBoxType b, String name);

    // the rest of the class: fields, methods
};

তারপরে একজন ফোন করবে:

Product p(Product::FANCY, Product::BOX);

তবে কোড সমাপ্তির প্রস্তাবগুলি দেখার সময় Product::, প্রায়শই সমস্ত সম্ভাব্য এনাম মানগুলি (বক্স, ফ্যানসি, ক্র্যাট) তালিকাভুক্ত হয়ে যায় এবং এখানে একটি ভুল করা সহজ (সি ++ 0 এক্স এর দৃ strongly়ভাবে টাইপযুক্ত এনামগুলি সমাধান করে, তবে কিছু মনে করবেন না) )।

তবে আপনি যদি নেস্টেড ক্লাসগুলি ব্যবহার করে সেই এনামগুলির জন্য অতিরিক্ত সুযোগ প্রবর্তন করেন তবে জিনিসগুলি দেখতে দেখতে দেখতে পারে:

class Product {
public:
    struct ProductType {
        enum Enum { FANCY, AWESOME, USEFUL };
    };
    struct ProductBoxType {
        enum Enum { BOX, BAG, CRATE };
    };
    Product(ProductType::Enum t, ProductBoxType::Enum b, String name);

    // the rest of the class: fields, methods
};

তারপরে কলটি দেখে মনে হচ্ছে:

Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);

তারপরে Product::ProductType::একটি আইডিই টাইপ করে, কেবলমাত্র পছন্দসই সুযোগ থেকে এনামগুলি পাওয়া যাবে। এটি ভুল করার ঝুঁকিও হ্রাস করে।

অবশ্যই এটি ছোট ক্লাসগুলির জন্য প্রয়োজন হতে পারে না তবে যদি কারও অনেকগুলি এনাম থাকে তবে এটি ক্লায়েন্ট প্রোগ্রামারদের জন্য জিনিসগুলিকে সহজ করে তোলে।

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

পিআইএমপিএল প্রতিমা


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

এটি সাধারণত নেস্টেড শ্রেণি ব্যবহার করে প্রয়োগ করা হয়:

Xh:

class X {
public:
    X();
    virtual ~X();
    void publicInterface();
    void publicInterface2();
private:
    struct Impl;
    std::unique_ptr<Impl> impl;
}

X.cpp:

#include "X.h"
#include <windows.h>

struct X::Impl {
    HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
    // all private fields, methods go here

    void privateMethod(HWND wnd);
    void privateMethod();
};

X::X() : impl(new Impl()) {
    // ...
}

// and the rest of definitions go here

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


3
struct Impl; std::auto_ptr<Impl> impl; এই ত্রুটিটি হার্ব সটার জনপ্রিয় করেছে। অসম্পূর্ণ প্রকারে অটো_সিটার ব্যবহার করবেন না, বা ভুল কোড উত্পন্ন হওয়ার জন্য অন্ততপক্ষে সাবধানতা অবলম্বন করুন।
জিন বুশুয়েভ

2
@ বিলি ওনিয়েল: যতদূর আমি অবগত আছি auto_ptrআপনি বেশিরভাগ বাস্তবায়নের ক্ষেত্রে অপূর্ণ একটি প্রকারের ঘোষণা করতে পারেন তবে প্রযুক্তিগতভাবে এটি সি ++ 0x (যেমন unique_ptr) এর কিছু টেম্পলেটগুলির থেকে আলাদা নয় যেখানে এটি স্পষ্ট করে দেওয়া হয়েছে যে টেমপ্লেট পরামিতি হতে পারে একটি অসম্পূর্ণ প্রকার এবং যেখানে ঠিক টাইপটি সম্পূর্ণ হতে হবে। (যেমন ব্যবহার ~unique_ptr)
সিবি বেইলি

2
@ বিলি ওনিল: সি ++ ০৩ ১.4.৪..6.৩ এ [lib.res.on.funitions] বলেছেন "বিশেষত, নিম্নলিখিত ক্ষেত্রে এর প্রভাবগুলি অপরিবর্তিত রয়েছে: [...] যদি কোনও অসম্পূর্ণ প্রকারটি টেম্পলেট যুক্তি হিসাবে ব্যবহার করা হয় কোনও টেম্পলেট উপাদান ইনস্ট্যান্ট করার সময় "। যদিও সি ++ 0 এক্সে এটি বলা হয়েছে "যদি কোনও টেম্পলেট উপাদানটি ইনস্ট্যান্ট করার সময় কোনও অসম্পূর্ণ প্রকারটি যদি টেম্পলেট যুক্তি হিসাবে ব্যবহৃত হয় তবে নির্দিষ্টভাবে সেই উপাদানটির জন্য অনুমতি না দেওয়া হয়" " এবং পরে (যেমন): "টেমপ্লেট প্যারামিটার Tএর unique_ptrএকটি অসম্পূর্ণ ধরনের হতে পারে।"
সিবি বেইলি

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

2
@ আইসাকপ্যাসিকুয়াল ওউ, আমার এখনই এটি আপডেট করা উচিত enum class
কোস

21

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

উদাহরণস্বরূপ, একটি জেনেরিক Fieldক্লাস বিবেচনা করুন যার আইডি নম্বর, একটি টাইপ কোড এবং একটি ক্ষেত্রের নাম রয়েছে। আমি যদি আইডি নম্বর বা নাম দ্বারা vectorএইগুলির মধ্যে একটি অনুসন্ধান করতে চাই Field, তবে আমি এটি করতে একটি ফান্টেক্টর তৈরি করতে পারি:

class Field
{
public:
  unsigned id_;
  string name_;
  unsigned type_;

  class match : public std::unary_function<bool, Field>
  {
  public:
    match(const string& name) : name_(name), has_name_(true) {};
    match(unsigned id) : id_(id), has_id_(true) {};
    bool operator()(const Field& rhs) const
    {
      bool ret = true;
      if( ret && has_id_ ) ret = id_ == rhs.id_;
      if( ret && has_name_ ) ret = name_ == rhs.name_;
      return ret;
    };
    private:
      unsigned id_;
      bool has_id_;
      string name_;
      bool has_name_;
  };
};

তারপরে এই কোডগুলির জন্য অনুসন্ধানের কোডটি শ্রেণীর মধ্যেই স্কোপড Fieldব্যবহার করতে পারে :matchField

vector<Field>::const_iterator it = find_if(fields.begin(), fields.end(), Field::match("FieldName"));

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

1
@ ব্যবহারকারী: কোনও এসটিএল ফান্টারের ক্ষেত্রে, নির্মাণকারীকে সর্বজনীন করা দরকার।
জন ডিবলিং

1
@ বিলি: নেস্টেড ক্লাসগুলি খারাপ কেন এমন কোনও যুক্তিসঙ্গত যুক্তি এখনও আমি দেখতে পাইনি।
জন ডিবলিং

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

1
অবশ্যই ম্যাক্রোগুলিতে ইনলাইনগুলি পছন্দ করার প্রযুক্তিগত কারণ রয়েছে !!
মাইলস রাউট

14

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

class Product{
    public:
        class Builder;
}
class Product::Builder {
    // Builder Implementation
}

বরং:

class Product {}
class ProductBuilder {}

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