C ++のクラス初期化子でconst配列を初期化します
-
03-07-2019 - |
質問
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
に異なる値を設定できる場合ですが、値はインスタンスの存続期間中一定であることがわかっています。
解決
他の人が言ったように、ISO C ++はそれをサポートしていません。しかし、あなたはそれを回避することができます。代わりにstd :: vectorを使用してください。
int* a = new int[N];
// fill a
class C {
const std::vector<int> v;
public:
C():v(a, a+N) {}
};
他のヒント
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;
}
現在の標準では不可能です。初期化リストを使用して、C ++ 0xでこれを行うことができると思います(簡単な外観を参照してください) Bjarne StroustrupによるC ++ 0x で、初期化リストとその他の優れたC ++ 0x機能の詳細を参照してください。
std :: vector
はヒープを使用します。 Geez、 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;
}
ConstFixedSizeArrayFiller
および ConstFixedSizeArray
は再利用可能です。
1つ目は、配列の初期化中に実行時の境界チェックを許可します(ベクトルと同じ)。この初期化の後、後で const
になります。
2番目の方法では、別のオブジェクトを内部に割り当てることができます。オブジェクトは、ヒープ上にある場合もあれば、単にスタック上にある場合もあります。ヒープから割り当てる時間の無駄はありません。また、配列に対してコンパイル時のconstチェックを実行します。
b_filler
は、初期化値を提供する小さなプライベートクラスです。配列のサイズはコンパイル時にテンプレート引数でチェックされるため、範囲外になる可能性はありません。
これを変更するためのもっとエキゾチックな方法があると確信しています。これは最初の刺し傷です。コンパイラのクラスの欠点をほとんど補うことができると思います。
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;
}
定数配列の場合、常に静的に行われます。それを受け入れることができる場合、このコードはコンパイルして実行する必要があります。
#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;
}
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){};
};
アクセサー関数を介して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を継承することで実行できます。しかし、 John Harrisonのcomp.langを好むと思います。 constクラスを使用したc ++投稿。
興味深いことに、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で変更する場合はコメントを付けて変更します。