constructor tĩnh trong C ++? Tôi cần khởi tạo các đối tượng tĩnh riêng


176

Tôi muốn có một lớp với một thành viên dữ liệu tĩnh riêng (một vectơ chứa tất cả các ký tự az). Trong java hoặc C #, tôi chỉ có thể tạo một "hàm tạo tĩnh" sẽ chạy trước khi tôi thực hiện bất kỳ phiên bản nào của lớp và thiết lập các thành viên dữ liệu tĩnh của lớp. Nó chỉ được chạy một lần (vì các biến chỉ được đọc và chỉ cần được đặt một lần) và vì đây là chức năng của lớp nên nó có thể truy cập các thành viên riêng của nó. Tôi có thể thêm mã trong hàm tạo để kiểm tra xem vectơ có được khởi tạo không và khởi tạo nó nếu không, nhưng điều đó đưa ra nhiều kiểm tra cần thiết và dường như không phải là giải pháp tối ưu cho vấn đề.

Tôi nghĩ rằng vì các biến sẽ chỉ được đọc, chúng chỉ có thể là hằng tĩnh công khai, vì vậy tôi có thể đặt chúng một lần bên ngoài lớp, nhưng một lần nữa, nó có vẻ giống như một hack xấu xí.

Có thể có các thành viên dữ liệu tĩnh riêng trong một lớp nếu tôi không muốn khởi tạo chúng trong hàm tạo cá thể không?



1
@CiroSantilli 改造 心 心 question question Câu hỏi này tập trung vào việc chạy mã để khởi tạo các đối tượng tĩnh riêng , không đặt giá trị không đổi của các kiểu nguyên thủy tĩnh riêng. Các giải pháp là khác nhau.
Gordon Gustafson

ah, tôi nghĩ bạn đúng, rút ​​lại.
Ciro Santilli 郝海东 冠状 病

Câu trả lời:


180

Để có được tương đương với một hàm tạo tĩnh, bạn cần viết một lớp bình thường riêng để giữ dữ liệu tĩnh và sau đó tạo một thể hiện tĩnh của lớp thông thường đó.

class StaticStuff
{
     std::vector<char> letters_;

public:
     StaticStuff()
     {
         for (char c = 'a'; c <= 'z'; c++)
             letters_.push_back(c);
     }

     // provide some way to get at letters_
};

class Elsewhere
{
    static StaticStuff staticStuff; // constructor runs once, single instance

};

12
cảm ơn! mặc dù điều đó rất khó chịu khi phải làm tất cả điều đó Một trong nhiều "lỗi" C # và java học được từ.
Gordon Gustafson

109
Đúng. Tôi luôn chỉ ra cho mọi người rằng nếu C ++ không mắc phải tất cả những "lỗi lầm" đó thì các ngôn ngữ khác sẽ phải mắc phải chúng. C ++ bao trùm rất nhiều nền tảng, thậm chí phạm sai lầm, đã rất tuyệt vời cho các ngôn ngữ đi theo nó.
quark

11
Chỉ cần một sắc thái nhỏ, khi các nhà xây dựng đi vào hoạt động, không ai đảm bảo khi nhà xây dựng cho đối tượng tĩnh thực thi. Một cách tiếp cận nổi tiếng an toàn hơn nhiều là lớp Elsewhere {StaticStuff & get_staticStuff () {static staticStuff staticStuff; // constructor chạy một lần, khi ai đó cần lần đầu tiên trả về staticStuff; }}; Tôi tự hỏi liệu các hàm tạo tĩnh trong C # và Java có thể cung cấp sự bảo đảm giống như mã ở trên không ...
Oleg Zhylin

13
@Oleg: Có họ làm. Các gurantees tiêu chuẩn mà các hàm tạo cho tất cả các biến không cục bộ được thực thi trước khi nhập chính. Nó cũng đảm bảo rằng trong một đơn vị biên dịch, thứ tự xây dựng được xác định rõ và thứ tự giống như khai báo trong đơn vị biên dịch. Thật không may, họ không xác định thứ tự trên nhiều đơn vị biên dịch.
Martin York

13
Đây thực sự là một trường hợp có friendý nghĩa rất lớn để lớp Elsewherecó thể dễ dàng truy cập vào StaticStuffbên trong (mà không phá vỡ đóng gói theo bất kỳ cách nguy hiểm nào, tôi có thể thêm vào).
Konrad Rudolph

81

Vâng, bạn có thể có

class MyClass
{
    public:
        static vector<char> a;

        static class _init
        {
          public:
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;
};

Đừng quên (trong .cpp) này:

vector<char> MyClass::a;
MyClass::_init MyClass::_initializer;

Chương trình vẫn sẽ liên kết mà không có dòng thứ hai, nhưng trình khởi tạo sẽ không được thực thi.


+1 (không thử) Nhưng: Khi nào thì ctor _init._init () được gọi? Trước hoặc sau ctor của MyClass khi tôi có một đối tượng MyClass tĩnh? Tôi đoán bạn không thể nói ...
.

2
xin chào, tôi có thể tìm thêm ở đâu về phép thuật "khởi tạo" này?
Karel Bílek

Không nên MyClass::a.push_back(i)thay thế a.push_back(i)?
Neel Basu

4
@ur.: _initializerlà một tiểu dự án của MyClass. Các tiểu dự án được khởi tạo theo thứ tự này: các tiểu dự án lớp cơ sở ảo, theo thứ tự chiều sâu, từ trái sang phải (nhưng chỉ khởi tạo mỗi tiểu dự án riêng biệt một lần); sau đó các tiểu dự án lớp cơ sở đơn giản, theo thứ tự chiều sâu, từ trái sang phải; sau đó các tiểu dự án thành viên theo thứ tự khai báo. Vì vậy, an toàn khi sử dụng chiến lược của EFraim, miễn là mã đó _initialiserchỉ đề cập đến các thành viên được khai báo trước đó.
j_random_hacker

2
Câu trả lời này tốt hơn câu trả lời được chấp nhận vì tác giả đã đề cập đến việc khởi tạo không thể thiếu trên clip mã thứ hai.
Jeff T.

33

Giải pháp C ++ 11

Kể từ C ++ 11, bạn chỉ cần sử dụng các biểu thức lambda để khởi tạo các thành viên lớp tĩnh. Điều này thậm chí hoạt động nếu bạn cần áp đặt một trật tự xây dựng giữa các thành viên tĩnh khác nhau hoặc nếu bạn có các thành viên tĩnh const.

Tập tin tiêu đề:

class MyClass {
    static const vector<char> letters;
    static const size_t letterCount;
};

Nguồn tập tin:

// Initialize MyClass::letters by using a lambda expression.
const vector<char> MyClass::letters = [] {
    vector<char> letters;
    for (char c = 'a'; c <= 'z'; c++)
        letters.push_back(c);
    return letters;
}();

// The initialization order of static members is defined by the order of
// definition within the source file, so we can access MyClass::letters here.
const size_t MyClass::letterCount = letters.size();

giải pháp thú vị. trong trường hợp này nếu tôi ném một ngoại lệ ai có thể bắt nó?
rafi wiener

5
Mã khởi tạo chương trình tĩnh không bao giờ được đưa ra bất kỳ ngoại lệ nào, nếu không chương trình sẽ bị sập. Bạn phải bọc logic trình khởi tạo thành một try catchkhối nếu ngoại lệ có thể bị ném.
emkey08

19

Trong tệp .h:

class MyClass {
private:
    static int myValue;
};

Trong tệp .cpp:

#include "myclass.h"

int MyClass::myValue = 0;

5
Điều này hoạt động tốt cho các thành viên tĩnh cá nhân (bất kể loại). Sự thiếu sót so với các nhà xây dựng tĩnh là bạn không thể áp đặt một trật tự giữa các thành viên tĩnh khác nhau. Nếu bạn cần làm điều đó, hãy xem câu trả lời của Earwicker.
quark

Tôi đang làm chính xác điều đó, nhưng nó vẫn không biên dịch. Và nó nói đây là khu vực có vấn đề (trong hàm tạo, không phải tiêu đề)
Flotolk

14

Đây là một cách tiếp cận khác tương tự như Daniel Earwicker, cũng sử dụng gợi ý lớp bạn của Konrad Rudolph. Ở đây chúng tôi sử dụng một lớp tiện ích bạn bè riêng bên trong để khởi tạo các thành viên tĩnh của lớp chính của bạn. Ví dụ:

Tập tin tiêu đề:

class ToBeInitialized
{
    // Inner friend utility class to initialize whatever you need

    class Initializer
    {
    public:
        Initializer();
    };

    friend class Initializer;

    // Static member variables of ToBeInitialized class

    static const int numberOfFloats;
    static float *theFloats;

    // Static instance of Initializer
    //   When this is created, its constructor initializes
    //   the ToBeInitialized class' static variables

    static Initializer initializer;
};

Hồ sơ thực hiện:

// Normal static scalar initializer
const int ToBeInitialized::numberOfFloats = 17;

// Constructor of Initializer class.
//    Here is where you can initialize any static members
//    of the enclosing ToBeInitialized class since this inner
//    class is a friend of it.

ToBeInitialized::Initializer::Initializer()
{
    ToBeInitialized::theFloats =
        (float *)malloc(ToBeInitialized::numberOfFloats * sizeof(float));

    for (int i = 0; i < ToBeInitialized::numberOfFloats; ++i)
        ToBeInitialized::theFloats[i] = calculateSomeFancyValue(i);
}

Cách tiếp cận này có lợi thế là che giấu hoàn toàn lớp Trình khởi tạo khỏi thế giới bên ngoài, giữ cho mọi thứ trong lớp được khởi tạo.


+1 Để đưa ra một ví dụ giữ cho việc thực hiện trong tệp riêng của mình.
Andrew Larsson

1
Ngoài ra, bạn phải chắc chắn rằng nó ToBeInitialized::Initializer::Initializer()được gọi, vì vậy bạn cần thêm ToBeInitialized::Initializer ToBeInitialized::initializer;vào tệp thực hiện. Tôi đã lấy một số thứ từ ý tưởng của bạn và từ ý tưởng của EFraim, và nó hoạt động chính xác như tôi cần và trông sạch sẽ. Cảm ơn, anh bạn.
Andrew Larsson

11

Test::StaticTest() được gọi chính xác một lần trong quá trình khởi tạo tĩnh toàn cầu.

Người gọi chỉ phải thêm một dòng vào hàm là hàm tạo tĩnh của chúng.

static_constructor<&Test::StaticTest>::c;lực lượng khởi tạo ctrong quá trình khởi tạo tĩnh toàn cầu.

template<void(*ctor)()>
struct static_constructor
{
    struct constructor { constructor() { ctor(); } };
    static constructor c;
};

template<void(*ctor)()>
typename static_constructor<ctor>::constructor static_constructor<ctor>::c;

/////////////////////////////

struct Test
{
    static int number;

    static void StaticTest()
    {
        static_constructor<&Test::StaticTest>::c;

        number = 123;
        cout << "static ctor" << endl;
    }
};

int Test::number;

int main(int argc, char *argv[])
{
    cout << Test::number << endl;
    return 0;
}

Đây là một giải pháp tuyệt vời. Tôi thực sự thích câu trả lời của Douglas Mandel , nhưng điều này thậm chí còn ngắn gọn hơn.
FlintZA

Điều này thực sự tuyệt vời!
nh_

9

Không cần một init()chức năng, std::vectorcó thể được tạo từ một phạm vi:

// h file:
class MyClass {
    static std::vector<char> alphabet;
// ...
};

// cpp file:
#include <boost/range.hpp>
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
std::vector<char> MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) );

Tuy nhiên, lưu ý rằng các thống kê về loại lớp gây rắc rối trong các thư viện, vì vậy chúng nên được tránh ở đó.

Cập nhật C ++ 11

Kể từ C ++ 11, bạn có thể làm điều này thay thế:

// cpp file:
std::vector<char> MyClass::alphabet = { 'a', 'b', 'c', ..., 'z' };

Nó tương đương về mặt ngữ nghĩa với giải pháp C ++ 98 trong câu trả lời ban đầu, nhưng bạn không thể sử dụng một chuỗi ký tự ở phía bên tay phải, vì vậy nó không hoàn toàn vượt trội. Tuy nhiên, nếu bạn có một véc tơ của bất kỳ loại nào khác hơn char, wchar_t, char16_thoặc char32_t(mảng trong số đó có thể được viết như literals string), phiên bản C ++ 11 sẽ loại bỏ hoàn toàn mã boilerplate mà không giới thiệu cú pháp khác, so với C ++ 98 phiên bản.


Tôi thích nó. Mặc dù nếu chúng ta chỉ có thể làm điều đó trong một dòng mà không có bảng chữ cái bây giờ vô dụng.
Martin York

Để gây ra sự cố với các thư viện, có vấn đề gì không nếu lớp tĩnh là riêng tư hoặc công khai? Ngoài ra, có vấn đề gì nếu thư viện tĩnh (.a) hoặc động (.so) không?
Zachary Kraus

@ZacharyKraus: lớp học công / tư là gì? Và không, trong khi các vấn đề là khác nhau, nhưng chồng chéo, không quan trọng thư viện được liên kết tĩnh hay động.
Marc Mutz - mmutz

@ MarcMutz-mmutz Xin lỗi về việc sử dụng lớp công khai / riêng tư không đúng với thuật ngữ C ++. Những gì tôi đã đề cập đến là giải pháp của EFraim ở trên. Trong phiên bản của tôi, tôi đã đặt thành viên lớp tĩnh ở chế độ riêng tư. Tôi đã cố gắng để hiểu nếu việc có một thành viên lớp tĩnh là công khai hay riêng tư sẽ tạo ra sự khác biệt trong phát triển thư viện và khả năng sử dụng. Ruột của tôi nói với tôi rằng nó không nên ảnh hưởng đến thư viện bởi vì người dùng sẽ không bao giờ có quyền truy cập vào thành viên lớp tĩnh hoặc đối tượng xây dựng của nó, nhưng tôi rất muốn có được sự khôn ngoan của guru về chủ đề này.
Zachary Kraus

@ZacharyKraus: Vấn đề chính với các statics yêu cầu khởi tạo động ([basic.start.init] / 2) là chúng chạy mã. Trong các thư viện, có thể mã thư viện đã được tải khi các hàm hủy được chạy. Nếu bạn muốn nghe nhiều hơn, tôi đề nghị gửi một câu hỏi về nó.
Marc Mutz - mmutz 22/03/2015

6

Khái niệm về các hàm tạo tĩnh đã được giới thiệu trong Java sau khi chúng học được từ các vấn đề trong C ++. Vì vậy, chúng tôi không có tương đương trực tiếp.

Giải pháp tốt nhất là sử dụng các loại POD có thể được khởi tạo một cách rõ ràng.
Hoặc làm cho các thành viên tĩnh của bạn thành một loại cụ thể có hàm tạo riêng của nó sẽ khởi tạo nó một cách chính xác.

//header

class A
{
    // Make sure this is private so that nobody can missues the fact that
    // you are overriding std::vector. Just doing it here as a quicky example
    // don't take it as a recomendation for deriving from vector.
    class MyInitedVar: public std::vector<char>
    {
        public:
        MyInitedVar()
        {
           // Pre-Initialize the vector.
           for(char c = 'a';c <= 'z';++c)
           {
               push_back(c);
           }
        }
    };
    static int          count;
    static MyInitedVar  var1;

};


//source
int            A::count = 0;
A::MyInitedVar A::var1;

4

Khi cố gắng biên dịch và sử dụng lớp Elsewhere(từ câu trả lời của Earwicker ) tôi nhận được:

error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)

Dường như không thể khởi tạo các thuộc tính tĩnh của các loại không nguyên mà không đặt một số mã bên ngoài định nghĩa lớp (CPP).

Để thực hiện biên dịch đó, bạn có thể sử dụng " phương thức tĩnh với biến cục bộ tĩnh bên trong ". Một cái gì đó như thế này:

class Elsewhere
{
public:
    static StaticStuff& GetStaticStuff()
    {
        static StaticStuff staticStuff; // constructor runs once, single instance
        return staticStuff;
    }
};

Và bạn cũng có thể truyền đối số cho hàm tạo hoặc khởi tạo nó với các giá trị cụ thể, nó rất linh hoạt, mạnh mẽ và dễ thực hiện ... điều duy nhất là bạn có một phương thức tĩnh chứa biến tĩnh, không phải thuộc tính tĩnh ... cú pháp thay đổi một chút, nhưng vẫn hữu ích. Hy vọng điều này hữu ích cho ai đó,

Hugo González Fidel.


Mặc dù cẩn thận nếu sử dụng chủ đề. Tôi tin vào GCC, việc xây dựng các địa phương tĩnh được bảo vệ chống lại việc thực thi đồng thời, nhưng trong Visual C ++ thì không.
Daniel Earwicker

1
Từ C ++ 11 trở đi, và trong POSIX, nó được thread-safe.
Marc Mutz - mmutz

Tôi khá thích hai giải pháp khác ở trên ( cái nàycái này ), nhưng giải pháp của bạn là giải pháp duy nhất đảm bảo khởi tạo các thống kê theo thứ tự chúng cần trên các thư viện. Tôi chỉ có một phương thức Instance tĩnh riêng như của bạn ở trên và bao bọc quyền truy cập vào các giá trị khác trong các bộ truy cập tĩnh công khai sử dụng phương thức Instance đó thay vì tham chiếu trực tiếp. Cảm ơn.
FlintZA

Tuyệt vời! Điều này hoàn thành nó.
Gabe Halsmer

4

Tôi đoán giải pháp đơn giản cho việc này sẽ là:

    //X.h
    #pragma once
    class X
    {
    public:
            X(void);
            ~X(void);
    private:
            static bool IsInit;
            static bool Init();
    };

    //X.cpp
    #include "X.h"
    #include <iostream>

    X::X(void)
    {
    }


    X::~X(void)
    {
    }

    bool X::IsInit(Init());
    bool X::Init()
    {
            std::cout<< "ddddd";
            return true;
    }

    // main.cpp
    #include "X.h"
    int main ()
    {
            return 0;
    }

Đây là cách tôi làm điều đó, quá.
Etherealone

1

Chỉ cần giải quyết cùng một mẹo. Tôi đã phải xác định định nghĩa của một thành viên tĩnh duy nhất cho Singleton. Nhưng làm cho mọi thứ trở nên phức tạp hơn - tôi đã quyết định rằng tôi không muốn gọi ctor của RandClass () trừ khi tôi sẽ sử dụng nó ... đó là lý do tại sao tôi không muốn khởi tạo singleton trên toàn cầu trong mã của mình. Ngoài ra tôi đã thêm giao diện đơn giản trong trường hợp của tôi.

Đây là mã cuối cùng:

Tôi đã đơn giản hóa mã và sử dụng hàm rand () và hàm khởi tạo hạt giống duy nhất của nó srand ()

interface IRandClass
{
 public:
    virtual int GetRandom() = 0;
};

class RandClassSingleton
{
private:
  class RandClass : public IRandClass
  {
    public:
      RandClass()
      {
        srand(GetTickCount());
      };

     virtual int GetRandom(){return rand();};
  };

  RandClassSingleton(){};
  RandClassSingleton(const RandClassSingleton&);

  // static RandClass m_Instance;

  // If you declare m_Instance here you need to place
  // definition for this static object somewhere in your cpp code as
  // RandClassSingleton::RandClass RandClassSingleton::m_Instance;

  public:

  static RandClass& GetInstance()
  {
      // Much better to instantiate m_Instance here (inside of static function).
      // Instantiated only if this function is called.

      static RandClass m_Instance;
      return m_Instance;
  };
};

main()
{
    // Late binding. Calling RandClass ctor only now
    IRandClass *p = &RandClassSingleton::GetInstance();
    int randValue = p->GetRandom();
}
abc()
{
    IRandClass *same_p = &RandClassSingleton::GetInstance();
}

1

Đây là biến thể của tôi về giải pháp của EFraim; sự khác biệt là, nhờ khởi tạo khuôn mẫu ẩn, hàm tạo tĩnh chỉ được gọi nếu các thể hiện của lớp được tạo và không cần định nghĩa trong .cpptệp (nhờ vào phép thuật khởi tạo mẫu).

Trong .htệp, bạn có:

template <typename Aux> class _MyClass
{
    public:
        static vector<char> a;
        _MyClass() {
            (void) _initializer; //Reference the static member to ensure that it is instantiated and its initializer is called.
        }
    private:
        static struct _init
        {
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;

};
typedef _MyClass<void> MyClass;

template <typename Aux> vector<char> _MyClass<Aux>::a;
template <typename Aux> typename _MyClass<Aux>::_init _MyClass<Aux>::_initializer;

Trong .cpptệp, bạn có thể có:

void foobar() {
    MyClass foo; // [1]

    for (vector<char>::iterator it = MyClass::a.begin(); it < MyClass::a.end(); ++it) {
        cout << *it;
    }
    cout << endl;
}

Lưu ý rằng chỉ MyClass::ađược khởi tạo nếu có dòng [1], bởi vì nó gọi (và yêu cầu khởi tạo) của hàm tạo, sau đó yêu cầu khởi tạo _initializer.


1

Đây là một phương thức khác, trong đó vectơ là riêng tư đối với tệp chứa triển khai bằng cách sử dụng một không gian tên ẩn danh. Nó hữu ích cho những thứ như bảng tra cứu riêng tư khi triển khai:

#include <iostream>
#include <vector>
using namespace std;

namespace {
  vector<int> vec;

  struct I { I() {
    vec.push_back(1);
    vec.push_back(3);
    vec.push_back(5);
  }} i;
}

int main() {

  vector<int>::const_iterator end = vec.end();
  for (vector<int>::const_iterator i = vec.begin();
       i != end; ++i) {
    cout << *i << endl;
  }

  return 0;
}

Mặc dù bạn có thể muốn đặt tên Iimột cái gì đó tối nghĩa hơn một chút để bạn không vô tình sử dụng chúng ở đâu đó thấp hơn trong tệp.
Jim Hunziker

1
Thành thật mà nói, thật khó để hiểu tại sao bất cứ ai cũng muốn sử dụng các thành viên tĩnh riêng tư hơn là các không gian tên không đồng nhất trong các tệp thực hiện.
Jim Hunziker

1

Nó chắc chắn không cần phải phức tạp như câu trả lời hiện được chấp nhận (bởi Daniel Earwicker). Lớp học là thừa. Không cần chiến tranh ngôn ngữ trong trường hợp này.

tập tin .hpp:

vector<char> const & letters();

tập tin .cpp:

vector<char> const & letters()
{
  static vector<char> v = {'a', 'b', 'c', ...};
  return v;
}


0

Bạn định nghĩa các biến thành viên tĩnh tương tự như cách bạn xác định các phương thức thành viên.

foo.h

class Foo
{
public:
    void bar();
private:
    static int count;
};

foo.cpp

#include "foo.h"

void Foo::bar()
{
    // method definition
}

int Foo::count = 0;

2
Câu hỏi CrazyJugglerDrummer không phải là về kiểu dữ liệu cũ đơn giản tĩnh :)
jww

0

Để khởi tạo một biến tĩnh, bạn chỉ cần thực hiện bên trong tệp nguồn. Ví dụ:

//Foo.h
class Foo
{
 private:
  static int hello;
};


//Foo.cpp
int Foo::hello = 1;

Câu hỏi CrazyJugglerDrummer không phải là về kiểu dữ liệu cũ đơn giản tĩnh :)
jww

0

Cách tạo mẫu để bắt chước hành vi của C #.

template<class T> class StaticConstructor
{
    bool m_StaticsInitialised = false;

public:
    typedef void (*StaticCallback)(void);

    StaticConstructor(StaticCallback callback)
    {
        if (m_StaticsInitialised)
            return;

        callback();

        m_StaticsInitialised = true;
    }
}

template<class T> bool StaticConstructor<T>::m_StaticsInitialised;

class Test : public StaticConstructor<Test>
{
    static std::vector<char> letters_;

    static void _Test()
    {
        for (char c = 'a'; c <= 'z'; c++)
            letters_.push_back(c);
    }

public:
    Test() : StaticConstructor<Test>(&_Test)
    {
        // non static stuff
    };
};

0

Đối với các trường hợp đơn giản như ở đây, một biến tĩnh được bọc bên trong hàm thành viên tĩnh gần như là tốt. Nó đơn giản và thường sẽ được tối ưu hóa bởi trình biên dịch. Điều này không giải quyết vấn đề thứ tự khởi tạo cho các đối tượng phức tạp mặc dù.

#include <iostream>

class MyClass 
{

    static const char * const letters(void){
        static const char * const var = "abcdefghijklmnopqrstuvwxyz";
        return var;
    }

    public:
        void show(){
            std::cout << letters() << "\n";
        }
};


int main(){
    MyClass c;
    c.show();
}

0

Đây có phải là một giải pháp?

class Foo
{
public:
    size_t count;
    Foo()
    {
        static size_t count = 0;
        this->count = count += 1;
    }
};

0

Một hàm tạo tĩnh có thể được mô phỏng bằng cách sử dụng một lớp bạn hoặc lớp lồng nhau như dưới đây.

class ClassStatic{
private:
    static char *str;
public:
    char* get_str() { return str; }
    void set_str(char *s) { str = s; }
    // A nested class, which used as static constructor
    static class ClassInit{
    public:
        ClassInit(int size){ 
            // Static constructor definition
            str = new char[size];
            str = "How are you?";
        }
    } initializer;
};

// Static variable creation
char* ClassStatic::str; 
// Static constructor call
ClassStatic::ClassInit ClassStatic::initializer(20);

int main() {
    ClassStatic a;
    ClassStatic b;
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    a.set_str("I am fine");
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    std::cin.ignore();
}

Đầu ra:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine

Tại sao bạn newchỉ lấy một mảng char để ngay lập tức rò rỉ con trỏ và ghi đè lên nó!?
Eric

0

Ồ, tôi không thể tin rằng không ai đề cập đến câu trả lời rõ ràng nhất và câu trả lời gần giống nhất với hành vi xây dựng tĩnh của C #, tức là nó không được gọi cho đến khi đối tượng đầu tiên của loại đó được tạo.

std::call_once()có sẵn trong C ++ 11; nếu bạn không thể sử dụng nó, nó có thể được thực hiện với một biến lớp boolean tĩnh và một hoạt động nguyên tử so sánh và trao đổi. Trong hàm tạo của bạn, hãy xem liệu bạn có thể thay đổi nguyên bản cờ tĩnh lớp từ falsesang truehay không và nếu có, bạn có thể chạy mã xây dựng tĩnh.

Để có thêm tín dụng, hãy biến nó thành cờ 3 chiều thay vì boolean, tức là không chạy, chạy và chạy xong. Sau đó, tất cả các thể hiện khác của lớp đó có thể quay vòng khóa cho đến khi cá thể chạy hàm tạo tĩnh kết thúc (nghĩa là tạo một hàng rào bộ nhớ, sau đó đặt trạng thái thành "chạy xong"). Khóa xoay của bạn sẽ thực hiện hướng dẫn "tạm dừng" của bộ xử lý, tăng gấp đôi thời gian chờ mỗi lần cho đến khi đạt ngưỡng, v.v. - kỹ thuật khóa xoay khá chuẩn.

Trong trường hợp không có C ++ 11, điều này sẽ giúp bạn bắt đầu.

Đây là một số mã giả để hướng dẫn bạn. Đặt điều này trong định nghĩa lớp học của bạn:

enum EStaticConstructor { kNotRun, kRunning, kDone };
static volatile EStaticConstructor sm_eClass = kNotRun;

Và cái này trong constructor của bạn:

while (sm_eClass == kNotRun)
{
    if (atomic_compare_exchange_weak(&sm_eClass, kNotRun, kRunning))
    {
        /* Perform static initialization here. */

        atomic_thread_fence(memory_order_release);
        sm_eClass = kDone;
    }
}
while (sm_eClass != kDone)
    atomic_pause();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.