59493


C++で動的に配列を確保し、コンストラクタで初期値を与えようと思っているのですがうまくいかいず困っています  
以下のソースなのですがBaseClassをmain内で動的に配列を確保するまでは出来たのですが  
コンストラクタに引数を追加するとコンパイルエラーになります  
bcRec = new BaseClass(5)[n];やbcRec = new BaseClass(5)[n];  
と言った風にしてみたのですが・・・  
正しい文法はどのように記述したら良いのでしょう

**BaseClass.h**

[code]

    class BaseClass{
    private:
        int _a;
    public :
        BaseClass();
        BaseClass(int a);
        void setint(int a);
        int fooint();
    };
    
[/code]

**BaseClass.cpp**

[code]

    #include "BaseClass.h"
    BaseClass::BaseClass(int a){
        _a = a;
    }
    
    void BaseClass::setint(int a){
        _a = a;
    }
    
    BaseClass::BaseClass(){
        _a = 0;
    }
    
    int BaseClass::fooint(){
        return _a;
    }
    
[/code]

**main.cpp**

[code]

    int main(){
        BaseClass bc(10);
        BaseClass *bcRec;
        int n;
    
        n = 10;
        bcRec = new BaseClass[n]; //★本当は引数付きのコンストラクタを使いたい
    
        for (int i = 0; i < n; i++) {
            bcRec[i].setint(i * 10);
            std::cout << bcRec[i].fooint() << "\r\n";
        }
    }
    
[/code]


CC BY-SA 4.0
2019-10-05T14:46:51.613
59486
2019-10-07T04:12:54.500
15047
question
c++ array
C++で配列を動的に取りたい(コンストラクタに引数付きで)


`new BaseClass[n]`
とやっても`BaseClass`の配列はできますが`int`の配列にはなりません。`_a`はあくまでも1個の`int`ですので、これを動的配列にする必要があります。

C++での動的配列は、一般的には`vector`を使います。`int _a;`の代わりに`std::vector<int>
_a;`と書きます。vectorを使うには`#include <vector>`が必要です。そして`BaseClass`のコンストラクタを次のようにします。

[code]

    BaseClass::BaseClass(int a)
        : _a(a)
    {
    }
    
[/code]

これは、`_a`を構築するために`std::vector<int>
_a(a);`と書きたいところですが、`BaseClass`で自身のメンバ変数を初期化するための記述方法は上記のようになります。メンバ変数の初期化の文法についてはC++の参考書で勉強してください。

メンバ関数`setint`は次のようになります。

[code]

    void BaseClass::setint(int i, int a)
    {
        _a[i] = a;
    }
    
[/code]

メンバ関数`fooint`（`getint`?）は次のようになります。

[code]

    int BaseClass::fooint(int i)
    {
        return _a[i];
    }
    
[/code]

`_a`は`vector`ですので、`[]`で読み書きできます。`vector`の使い方は参考書で勉強してください。配列のインデックス値をメンバ関数の第一引数で渡している点を理解してください。

ここまでできれば、`main`関数をどう書き変えればいいかは想像できると思います。とりあえず`BaseClass
*bcRec;`は不要なので削除しましょう。あとはご自身で考えてみてください。

以下、改良のための余談。

個数を意味する変数名は`a`より`n`（`number`）の方が相応しいです。同様に、値を意味する変数名は`a`より`v`（`value`）などがいいでしょう。もちろん配列に何を格納するかによって、`value`よりもっと適した意味を持つ単語を使うと良いです。

`fooint`（`getint`）は読み取るだけで書き込みはしませんので`const`をつけるのが理想です。はじめのうちは無くてもいいでしょうが、今後、実用的なプログラムを作るのであれば`const`のテクニックはほぼ必須ですので、参考書で勉強してください。

あと、そもそも`BaseClass`を一切作らずに、`main`関数内で`std::vector<int>
array(5);`などと書いてやってみるのも理解の助けになると思います。

クラスメンバの変数を配列にしたいのか（上記の書き方）、オブジェクト（クラスのインスタンス）を配列にしたいのかで書き方が変わります。


CC BY-SA 4.0
2019-10-05T17:23:22.047
59489
2019-10-05T17:23:22.047
3337
59486
answer


前提条件があるので、いつでも使えるというわけではありませんが、動的な配列はC++では、`std::vector`が第一候補です。

[code]

    #include <iostream>
    #include <vector>
    #include "BaseClass.h"
    
    int main() {
        int n;
    
        n = 10;
        std::vector<BaseClass> bcRec; // 空のvectorを再生
        bcRec.reserve(n);             // n個分のメモリを確保するが初期化はしない
    
        // vectorの最後に、初期化をしながら(コンストラクタを呼びながら)
        // 一つずつBaseClassを追加
        for (int i = 0; i < n; ++i) {
            bcRec.emplace_back(i * 10);
        }
    
        // 中身を確認
        for (int i = 0; i < n; i++) {
            std::cout << bcRec[i].fooint() << '\n';
        }
    
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2019-10-05T19:37:45.493
59493
2019-10-05T19:37:45.493
3605
59486
answer


(不定個数の) 動的配列を `new[]`
したい、かつ各要素を「デフォルトでない値」で均一にコンストラクトしたい（代入でなく）とオイラはこの質問を読んだっす。

[code]

    int* a=new int[3]; // だと「３要素をデフォルト初期化」つまり 0 になる
    int* b=new int[3] /* ３要素に同じ初期値 42 を与えたい */ ; // どう書く？
    T* c=new T[3] /* 同じく３要素に同じ初期値(デフォルトでない)を与えたい */;
    
[/code]

たいていの「無いことの証明」は悪魔の証明なのですが、この件に関しては言語仕様書をさっくり見る限りにおいて **できません**
。できるような構文が用意されていません。

引用はいいんようということで C++ 言語仕様書 ISO/IEC 14882:2003 から引用

5.3.4 `new` 式の 15

> 型 `T` のオブジェクトを作る《 `new` 式》は (snip) 《生成初期化子》が `(式並び)` という形式 (snip) である場合、
> 適切なコンストラクタが呼び出される

つまり `T` 型の１つのオブジェクト（変数）を作る場合には初期値式並びが指定できます。

一方で、型 `T` のオブジェクトの配列を作る場合は言語仕様書に記載がありません。できないということです。

SuperBike
レース車両のレギュレーションブックは「認められると書かれてる変更はして良い」「認められると書かれていない項目は変更してはならない」と読む約束で、この場合も同様と考えます。

ココの項目には [c++11](/questions/tagged/c%2b%2b11 "'c++11' のタグが付いた質問を表示") にて修正が入っていて  
<https://ja.cppreference.com/w/cpp/language/new>  
集成体初期化ができるようになっています。  
<https://ja.cppreference.com/w/cpp/language/aggregate_initialization>  
が、この件については集成体初期化ではおそらく望みを達成することができません。

集成体とは「値が複数個含まれるものすべて」つまり配列も集成体ですし、クラスも集成体です。

[code]

    void func() {
        int x[64]={0};
    }
    
[/code]

これはよくある「自動変数配列のゼロ初期化」ですが、ここに集成体初期化が使われていて、コンパイラは次のように読む仕様です。  
\- `x[0]` の初期値として `0` が明示されている  
\- `x[1]` 以後は初期値が明示されていない  
\- このとき、明示されていない要素については値初期化を行う（端的には `0` にする）  
\- 結果的に全要素を `0` に初期化する挙動となる

なので  
\- C++03 の場合 `bcRec = new BaseClass[n] {42};` と書くことが認められていない  
\- C++11 の場合 `bcRec = new BaseClass[n] {42};` と書けるが  
\- `bcRec[0]` は `42` を受け取るコンストラクタで初期化されるが  
\- `bcRec[1]` 以後はデフォルトコンストラクトされる  
という挙動になります。

要素数が事前にわかっていれば `bcRec = new BaseClass[3] { 42, 42, 42 };`
と書けるわけですが、この例では可変個数なので無理っす。

可変要素数配列の２要素目以後のコンストラクタがうまく指定できないのは言語仕様上の欠陥ではあるのですが、集成体初期化は既にあるソースで広く使われているので
`Breaking Change` (既存ソースコードの動作挙動が変わる変更) に慎重な [c](/questions/tagged/c "'c'
のタグが付いた質問を表示") / [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
においてこの辺の仕様変更は今後もまず無いと考えていいでしょう。

オイラならこの件、コンストラクタによる初期化だけで処理するのはあきらめて、デフォルトコンストラクト後に代入します（だって無理だし）


CC BY-SA 4.0
2019-10-07T01:50:19.117
59520
2019-10-07T01:50:19.117
8589
59486
answer


`new`は次の２つの役割を持っています。

  * メモリ確保を行う
  * コンストラクターを呼び出す 
    * （非配列版）引数を指定できる
    * （配列版）引数を指定できず、デフォルトコンストラクターが呼ばれる

この質問では、配列版においてデフォルトコンストラクター以外を呼び出したい、とのことですが、とりあえずは実現できません。

ただし、[`new`](https://ja.cppreference.com/w/cpp/memory/new/operator_new)の機能を分割して、それぞれを呼び出せば実現可能です。  
`::operator new`は指定されたサイズのメモリを確保し、対になる`::operator delete`で解放できます。またplacement
new（配置new）は指定されたアドレス上でコンストラクターを実行します。

[code]

    // メモリ確保
    bcRec = static_cast<BaseClass*>(::operator new(sizeof(BaseClass) * n));
    // コンストラクター実行
    for (int i = 0; i < n; i++)
        ::new (bcRec + i)BaseClass(5);
    // デストラクター実行
    for (int i = 0; i < n; i++)
        bcRec[i].~BaseClass();
    // メモリ解放
    ::operator delete(bcRec);
    
[/code]

STLはこれを支援する関数を提供しています。

[code]

    std::allocator<BaseClass> al;
    // メモリ確保
    bcRec = al.allocate(n);
    // コンストラクター実行
    std::uninitialized_fill_n(bcRec, n, 5);
    // デストラクター実行
    std::destroy_n(bcRec, n);
    // メモリ解放
    al.deallocate(bcRec, n);
    
[/code]

とはいえ、上記コードは煩雑であり、要素数を誤ると簡単に不正な処理を行ってしまうため非常に危険です。STLに用意されている[`std::vector`](https://ja.cppreference.com/w/cpp/container/vector)で動的に確保することをお勧めします。特に`reserve()`で事前にメモリ確保しておくことで、要素数増減に伴うメモリ確保のオーバーヘッドを削減することができます。


CC BY-SA 4.0
2019-10-07T04:12:54.500
59521
2019-10-07T04:12:54.500
4236
59486
answer