web-dev-qa-db-ja.com

C ++のクラス初期化子でconst配列を初期化します

C++には次のクラスがあります。

class a {
    const int b[2];
    // other stuff follows

    // and here's the constructor
    a(void);
}

問題は、bがconstであるため、コンストラクタの関数の本体内でbを初期化できない場合、初期化リストでbをどのように初期化するかです。

これは機能しません:

a::a(void) : 
    b([2,3])
{
     // other initialization stuff
}

編集:適切なケースは、異なるインスタンスのbに異なる値を設定できる場合ですが、値はインスタンスの存続期間中一定であることがわかっています。

71
Nathan Fellman

他の人が言ったように、ISO C++はそれをサポートしていません。しかし、あなたはそれを回避することができます。代わりにstd :: vectorを使用してください。

int* a = new int[N];
// fill a

class C {
  const std::vector<int> v;
public:
  C():v(a, a+N) {}
};
32
Weipeng L

C++ 11では、この質問に対する答えが変更され、実際に次のことができます。

struct a {
    const int b[2];
    // other bits follow

    // and here's the constructor
    a();
};

a::a() :
    b{2,3}
{
     // other constructor work
}

int main() {
 a a;
}
72
Flexo

現在の標準では不可能です。初期化リストを使用して、C++ 0xでこれを行うことができると信じています(初期化リストとその他のNice Cの詳細については、Bjarne Stroustrupによる C++ 0xの概要 を参照してください) ++ 0x機能)。

25
Luc Touraille

std::vectorはヒープを使用します。ええと、const健全性チェックのためだけの無駄です。 std::vectorのポイントは、実行時に動的に増加することであり、コンパイル時に実行すべき古い構文チェックではありません。成長しない場合は、クラスを作成して通常の配列をラップします。

#include <stdio.h>


template <class Type, size_t MaxLength>
class ConstFixedSizeArrayFiller {
private:
    size_t length;

public:
    ConstFixedSizeArrayFiller() : length(0) {
    }

    virtual ~ConstFixedSizeArrayFiller() {
    }

    virtual void Fill(Type *array) = 0;

protected:
    void add_element(Type *array, const Type & element)
    {
        if(length >= MaxLength) {
            // todo: throw more appropriate out-of-bounds exception
            throw 0;
        }
        array[length] = element;
        length++;
    }
};


template <class Type, size_t Length>
class ConstFixedSizeArray {
private:
    Type array[Length];

public:
    explicit ConstFixedSizeArray(
        ConstFixedSizeArrayFiller<Type, Length> & filler
    ) {
        filler.Fill(array);
    }

    const Type *Array() const {
        return array;
    }

    size_t ArrayLength() const {
        return Length;
    }
};


class a {
private:
    class b_filler : public ConstFixedSizeArrayFiller<int, 2> {
    public:
        virtual ~b_filler() {
        }

        virtual void Fill(int *array) {
            add_element(array, 87);
            add_element(array, 96);
        }
    };

    const ConstFixedSizeArray<int, 2> b;

public:
    a(void) : b(b_filler()) {
    }

    void print_items() {
        size_t i;
        for(i = 0; i < b.ArrayLength(); i++)
        {
            printf("%d\n", b.Array()[i]);
        }
    }
};


int main()
{
    a x;
    x.print_items();
    return 0;
}

ConstFixedSizeArrayFillerConstFixedSizeArrayは再利用可能です。

1つ目は、配列の初期化中に実行時の境界チェックを可能にし(ベクトルと同じ)、この初期化後に後でconstになります。

2番目の方法では、配列をinside別のオブジェクトに割り当てることができます。これは、ヒープ上にあるか、オブジェクトが存在する場合は単にスタック上にある可能性があります。ヒープから割り当てる時間の無駄はありません。また、配列に対してコンパイル時のconstチェックを実行します。

b_fillerは、初期化値を提供する小さなプライベートクラスです。配列のサイズはコンパイル時にテンプレート引数でチェックされるため、範囲外になる可能性はありません。

これを変更するよりエキゾチックな方法があると確信しています。これは最初の刺し傷です。コンパイラのクラスの欠点をほとんど補うことができると思います。

12
Matthew

ISO標準C++では、これを行うことはできません。もしそうなら、構文はおそらく次のようになります。

a::a(void) :
b({2,3})
{
    // other initialization stuff
}

またはそれらの線に沿って何か。あなたの質問から、実際にあなたが望むのは、配列である定数クラス(別名静的)メンバーであるように聞こえます。 C++ではこれが可能です。そのようです:

#include <iostream>

class A 
{
public:
    A();
    static const int a[2];
};

const int A::a[2] = {0, 1};

A::A()
{
}

int main (int argc, char * const argv[]) 
{
    std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n";
    return 0;
}

出力は次のとおりです。

A::a => 0, 1

もちろん、これは静的なクラスメンバであるため、クラスAのすべてのインスタンスで同じです。それが望むものでない場合、つまりAの各インスタンスに配列aの異なる要素値を持たせたい場合は、そもそも配列をconstにしようとするミス。あなたはこれをしているだけです:

#include <iostream>

class A 
{
public:
    A();
    int a[2];
};

A::A()
{
    a[0] = 9; // or some calculation
    a[1] = 10; // or some calculation
}

int main (int argc, char * const argv[]) 
{
    A v;
    std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n";
    return 0;
}
9
orj

初期化リストからはできませんが、

これを見てください:

http://www.cprogramming.com/tutorial/initialization-lists-c++.html

:)

4
Trap

私が定数配列を持っている場合、それは常に静的に行われます。それを受け入れることができる場合、このコードはコンパイルして実行する必要があります。

#include <stdio.h>
#include <stdlib.h>

class a {
        static const int b[2];
public:
        a(void) {
                for(int i = 0; i < 2; i++) {
                        printf("b[%d] = [%d]\n", i, b[i]);
                }
        }
};

const int a::b[2] = { 4, 2 };

int main(int argc, char **argv)
{
        a foo;
        return 0;
}
4
Daniel Bungert

std::vectorでヒープを使用しないソリューションは、boost::arrayを使用することです。ただし、コンストラクターで配列メンバーを直接初期化することはできません。

#include <boost/array.hpp>

const boost::array<int, 2> aa={ { 2, 3} };

class A {
    const boost::array<int, 2> b;
    A():b(aa){};
};
3
CharlesB

アクセサー関数を介してconst配列をエミュレートするのはどうですか? (あなたが要求したように)非静的であり、stlや他のライブラリを必要としません:

class a {
    int privateB[2];
public:
    a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; }
    int b(const int idx) { return privateB[idx]; }
}

A :: privateBはprivateであるため、a ::の外側では事実上一定であり、配列と同様にアクセスできます。

a aobj(2,3);    // initialize "constant array" b[]
n = aobj.b(1);  // read b[1] (write impossible from here)

クラスのペアを使用する場合は、さらにprivateBをメンバー関数から保護できます。これは、aを継承することで実行できます。しかし、私はconstクラスを使用してジョン・ハリソンのcomp.lang.c ++投稿を好むと思う

3
Pete

興味深いことに、C#には、コンストラクタや初期化でのみ設定できるreadonlyとは対照的に、C++の静的constに変換されるキーワードconstがあります。

readonly DateTime a = DateTime.Now;

Constの事前定義済み配列がある場合は、静的にすることもできます。その時点で、この興味深い構文を使用できます。

//in header file
class a{
    static const int SIZE;
    static const char array[][10];
};
//in cpp file:
const int a::SIZE = 5;
const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"};

ただし、定数「10」を回避する方法は見つかりませんでした。理由は明らかですが、配列へのアクセスを実行する方法を知る必要があります。代わりの方法は#defineを使用することですが、その方法が嫌いで、ヘッダーの最後に#undefを付けて、CPPで編集するためのコメントを加えて、変更があった場合に備えています。

2
Nefzen