tutorial কিভাবে C++ মধ্যে ব্যক্তিগত স্ট্যাটিক সদস্যদের আরম্ভ করতে?




the c++ programming language (14)

C ++ এ একটি ব্যক্তিগত, স্ট্যাটিক ডেটা সদস্য আরম্ভ করার সেরা উপায় কী? আমি আমার হেডার ফাইল এই চেষ্টা, কিন্তু এটা আমাকে অদ্ভুত লিঙ্কার ত্রুটি দেয়:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

আমি এই অনুমান করছি কারণ আমি ক্লাসের বাইরে থেকে একটি প্রাইভেট সদস্য আরম্ভ করতে পারছি না। তাই এই কাজ করার সেরা উপায় কি?


একাধিক বস্তুর জন্য কাজ করে স্ট্যাটিক কন্সট্রাকটর প্যাটার্ন

একটি idiom প্রস্তাবিত হয়েছিল: https://.com/a/27088552/895245 কিন্তু এখানে একটি ক্লিনার সংস্করণ যা প্রত্যেক সদস্যের একটি নতুন পদ্ধতি তৈরি করার প্রয়োজন হয় না এবং একটি চালযোগ্য উদাহরণ:

#include <cassert>
#include <vector>

// Normally on the .hpp file.
class MyClass {
public:
    static std::vector<int> v, v2;
    static struct _StaticConstructor {
        _StaticConstructor() {
            v.push_back(1);
            v.push_back(2);
            v2.push_back(3);
            v2.push_back(4);
        }
    } _staticConstructor;
};

// Normally on the .cpp file.
std::vector<int> MyClass::v;
std::vector<int> MyClass::v2;
// Must come after every static member.
MyClass::_StaticConstructor MyClass::_staticConstructor;

int main() {
    assert(MyClass::v[0] == 1);
    assert(MyClass::v[1] == 2);
    assert(MyClass::v2[0] == 3);
    assert(MyClass::v2[1] == 4);
}

GitHub আপস্ট্রিম

আরও দেখুন: সি ++ স্ট্যাটিক কন্সট্রাক্টর? আমি ব্যক্তিগত স্ট্যাটিক বস্তু আরম্ভ করতে হবে

g++ -std=c++11 -Wall -Wextra , GCC 7.3, উবুন্টু 18.04 এর সাথে পরীক্ষা করা হয়েছে।


এই প্রশ্নের ভবিষ্যতের দর্শকদের জন্য, আমি নির্দেশ করতে চাই যে আপনাকে যে বানরটি সুপারিশ করা উচিত সেটি এড়াতে হবে।

শিরোনাম ফাইল ঘোষণা জন্য হয়।

হেডার ফাইলগুলি প্রতিটি। .cpp ফাইলের জন্য একবার একবার সংকলিত হয় যা সরাসরি বা পরোক্ষভাবে #includes করে এবং কোনও ফাংশনের বাইরে কোডটি main() আগে প্রোগ্রামের প্রাথমিককরণে চালানো হয়।

নির্বাণ করে: foo::i = VALUE; হেডারের মধ্যে, foo:i প্রতিটি। .cpp ফাইলের জন্য মান VALUE (যা যা কিছু) বরাদ্দ করা হবে এবং এই কাজগুলি main() চালানোর আগে একটি অনিশ্চিত ক্রম (লিঙ্ককারী দ্বারা নির্ধারিত main() হবে।

আমরা যদি আমাদের .cpp ফাইলগুলির মধ্যে একটিতে #define VALUE কে একটি ভিন্ন নম্বর হিসাবে নির্ধারণ করতে পারি? এটি জরিমানা কম্পাইল হবে এবং আমরা প্রোগ্রামটি চালানো না হওয়া পর্যন্ত কোনটি জিতেছে তা জানার কোন উপায় নেই।

আপনি কোন .cpp ফাইল #include না যে একই কারণে .cpp কোনও শিরোনামে কোডটি প্রয়োগ করবেন না।

গার্ডগুলি (যা আমি আপনাকে সবসময়ই ব্যবহার করতে সম্মত) অন্তর্ভুক্ত করি সেগুলি আপনাকে ভিন্ন কিছু থেকে রক্ষা করে: একই শিরোনাম পরোক্ষভাবে #include একটি একক। .cpp ফাইল কম্পাইল করার সময় একাধিক বার #include করুন


মাইক্রোসফ্ট কম্পাইলারের সাথে [1], স্ট্যাটিক ভেরিয়েবল যা int -like নয় তা শিরোনাম ফাইলের মধ্যেও সংজ্ঞায়িত করা যেতে পারে তবে __declspec(selectany) বাইরে Microsoft নির্দিষ্ট __declspec(selectany) ব্যবহার করে।

class A
{
    static B b;
}

__declspec(selectany) A::b;

মনে রাখবেন আমি এটা ভাল না বলছি, আমি শুধু এটা করা যেতে পারে বলে।

[1] এই দিন, এমএসসি সমর্থন তুলনায় আরো কম্পাইলার __declspec(selectany) সমর্থন - অন্তত __declspec(selectany) এবং __declspec(selectany) । হয়তো আরো।


আপনি যদি কিছু যৌগিক ধরন (fe স্ট্রিং) শুরু করতে চান তবে আপনি এমন কিছু করতে পারেন:

class SomeClass {
  static std::list<string> _list;

  public:
    static const std::list<string>& getList() {
      struct Initializer {
         Initializer() {
           // Here you may want to put mutex
           _list.push_back("FIRST");
           _list.push_back("SECOND");
           ....
         }
      }
      static Initializer ListInitializationGuard;
      return _list;
    }
};

ListInitializationGuard SomeClass::getList() পদ্ধতির ভিতরে একটি স্ট্যাটিক ভেরিয়েবল হিসাবে এটি শুধুমাত্র একবার তৈরি হবে, যার মানে কন্সট্রাকটর একবার বলা হয়। এই initialize _list পরিবর্তনশীল আপনি প্রয়োজন মূল্য initialize _list হবে। getList থেকে পরবর্তী কোনও কল কেবলমাত্র প্রাথমিকভাবে _list বস্তুটি ফেরত _list

অবশ্যই আপনি getList() পদ্ধতি কল করে _list অবজেক্টটি অ্যাক্সেস করতে হবে।


int foo::i = 0; 

পরিবর্তনশীল আরম্ভ করার জন্য সঠিক সিনট্যাক্স, তবে এটি হেডারের পরিবর্তে উৎস ফাইল (.cpp) এ যেতে হবে।

এটি একটি স্ট্যাটিক পরিবর্তনশীল কারণ কম্পাইলার এটি শুধুমাত্র একটি কপি তৈরি করতে হবে। আপনাকে একটি লাইন "int foo: i" থাকতে হবে যেখানে আপনার কোডটিতে কম্পাইলারকে বলার জন্য এটি কোথায় রাখতে হবে অন্যথায় আপনি একটি লিঙ্ক ত্রুটি পাবেন। যদি এটি শিরোনামে থাকে তবে শিরোনাম সহ প্রতিটি ফাইলে একটি অনুলিপি পাবেন, তাই লিঙ্কার থেকে সংজ্ঞায়িত প্রতীক ত্রুটিগুলি পান।


আমি কার্ল থেকে ধারণা অনুসরণ। আমি এটা পছন্দ করি এবং এখন আমি এটি ব্যবহার করি। আমি সংকেত সামান্য বিট পরিবর্তন করেছি এবং কিছু কার্যকারিতা যোগ করুন

#include <stdio.h>

class Foo
{
   public:

     int   GetMyStaticValue () const {  return MyStatic();  }
     int & GetMyStaticVar ()         {  return MyStatic();  }
     static bool isMyStatic (int & num) {  return & num == & MyStatic(); }

   private:

      static int & MyStatic ()
      {
         static int mStatic = 7;
         return mStatic;
      }
};

int main (int, char **)
{
   Foo obj;

   printf ("mystatic value %d\n", obj.GetMyStaticValue());
   obj.GetMyStaticVar () = 3;
   printf ("mystatic value %d\n", obj.GetMyStaticValue());

   int valMyS = obj.GetMyStaticVar ();
   int & iPtr1 = obj.GetMyStaticVar ();
   int & iPtr2 = valMyS;

   printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}

এই আউটপুট

mystatic value 7
mystatic value 3
is my static 1 0

সম্ভবত আপনি সম্মুখীন লিঙ্কার সমস্যা:

  • শিরোনাম ফাইল উভয় বর্গ এবং স্ট্যাটিক সদস্য সংজ্ঞা প্রদান,
  • দুই বা ততোধিক উত্স ফাইলের মধ্যে এই শিরোনাম সহ।

এটি তাদের জন্য একটি সাধারণ সমস্যা যারা C ++ দিয়ে শুরু হয়। স্ট্যাটিক ক্লাস সদস্য একক অনুবাদ ইউনিট অর্থাৎ একক উৎস ফাইলের মধ্যে শুরু করতে হবে।

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

class Foo
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};

এই আপনার উদ্দেশ্য করে তোলে?

//header file

struct MyStruct {
public:
    const std::unordered_map<std::string, uint32_t> str_to_int{
        { "a", 1 },
        { "b", 2 },
        ...
        { "z", 26 }
    };
    const std::unordered_map<int , std::string> int_to_str{
        { 1, "a" },
        { 2, "b" },
        ...
        { 26, "z" }
    };
    std::string some_string = "justanotherstring";  
    uint32_t some_int = 42;

    static MyStruct & Singleton() {
        static MyStruct instance;
        return instance;
    }
private:
    MyStruct() {};
};

//Usage in cpp file
int main(){
    std::cout<<MyStruct::Singleton().some_string<<std::endl;
    std::cout<<MyStruct::Singleton().some_int<<std::endl;
    return 0;
}

এছাড়াও PrivateStatic.cpp ফাইল কাজ করে:

#include <iostream>

using namespace std;

class A
{
private:
  static int v;
};

int A::v = 10; // possible initializing

int main()
{
A a;
//cout << A::v << endl; // no access because of private scope
return 0;
}

// g++ privateStatic.cpp -o privateStatic && ./privateStatic

হেডার গার্ড ব্যবহার করলে আপনি হেডার ফাইলটিতে অ্যাসাইনমেন্ট অন্তর্ভুক্ত করতে পারেন। আমি তৈরি করেছি একটি সি ++ লাইব্রেরি জন্য এই কৌশল ব্যবহার করেছেন। একই ফলাফল অর্জন করার আরেকটি উপায় স্ট্যাটিক পদ্ধতি ব্যবহার করা হয়। উদাহরণ স্বরূপ...

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

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


একটি "পুরানো স্কুল" ধ্রুবক সংজ্ঞায়িত করার উপায় একটি enum দ্বারা তাদের প্রতিস্থাপন করা হয়:

class foo
{
    private:
        enum {i = 0}; // default type = int
        enum: int64_t {HUGE = 1000000000000}; // may specify another type
};

এই ভাবে একটি সংজ্ঞা প্রদানের প্রয়োজন হয় না, এবং ধ্রুবক lvalue তৈরি করা এড়িয়ে চলতে থাকে যা আপনাকে কিছু মাথাব্যাথা সংরক্ষণ করতে পারে, যেমন আপনি যখন দুর্ঘটনাক্রমে ODR-use এটি ODR-use করেন।


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

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

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


C ++ 17 থেকে, স্ট্যাটিক সদস্যদের ইনলাইন কীওয়ার্ড দিয়ে শিরোনামে সংজ্ঞায়িত করা যেতে পারে।

http://en.cppreference.com/w/cpp/language/static

"একটি স্ট্যাটিক ডেটা সদস্যকে ইনলাইন ঘোষণা করা যেতে পারে। একটি ইনলাইন স্ট্যাটিক ডেটা সদস্য শ্রেণী সংজ্ঞায় সংজ্ঞায়িত করা যেতে পারে এবং একটি ডিফল্ট সদস্যের প্রাথমিক সূত্র নির্দিষ্ট করতে পারে। এটি একটি শ্রেণিবদ্ধ শ্রেণীর সংজ্ঞা প্রয়োজন নেই:"

struct X
{
    inline static int n = 1;
};

একটি set_default() পদ্ধতি সম্পর্কে কি?

class foo
{
    public:
        static void set_default(int);
    private:
        static int i;
};

void foo::set_default(int x) {
    i = x;
}

আমরা শুধুমাত্র set_default(int x) পদ্ধতি ব্যবহার করতে হবে এবং আমাদের static পরিবর্তনশীল শুরু হবে।

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





static-members