web-dev-qa-db-ja.com

デフォルトのコンストラクターを使用しないオブジェクト配列の初期化

#include <iostream>
class Car
{
private:
  Car(){};
  int _no;
public:
  Car(int no)
  {
    _no=no;
  }
  void printNo()
  {
    std::cout<<_no<<std::endl;
  }
};
void printCarNumbers(Car *cars, int length)
{
    for(int i = 0; i<length;i++)
         std::cout<<cars[i].printNo();
}

int main()
{
  int userInput = 10;
  Car *mycars = new Car[userInput];
  for(int i =0;i < userInput;i++)
         mycars[i]=new Car[i+1];
  printCarNumbers(mycars,userInput);
  return 0;
}    

車の配列を作成したいのですが、次のエラーが表示されます。

cartest.cpp: In function ‘int main()’:
cartest.cpp:5: error: ‘Car::Car()’ is private
cartest.cpp:21: error: within this context

car()コンストラクタを公開せずにこの初期化を行う方法はありますか?

57
Dan Paradox

いや。

しかし、lo! std::vector<Car>を使用する必要がある場合(new[]を使用しないでください)、要素の作成方法を正確に指定できます*。

*まあまあ。コピーする値を指定できます。


このような:

#include <iostream>
#include <vector>

class Car
{
private:
    Car(); // if you don't use it, you can just declare it to make it private
    int _no;
public:
    Car(int no) :
    _no(no)
    {
        // use an initialization list to initialize members,
        // not the constructor body to assign them
    }

    void printNo()
    {
        // use whitespace, itmakesthingseasiertoread
        std::cout << _no << std::endl;
    }
};

int main()
{
    int userInput = 10;

    // first method: userInput copies of Car(5)
    std::vector<Car> mycars(userInput, Car(5)); 

    // second method:
    std::vector<Car> mycars; // empty
    mycars.reserve(userInput); // optional: reserve the memory upfront

    for (int i = 0; i < userInput; ++i)
        mycars.Push_back(Car(i)); // ith element is a copy of this

    // return 0 is implicit on main's with no return statement,
    // useful for snippets and short code samples
} 

追加機能:

void printCarNumbers(Car *cars, int length)
{
    for(int i = 0; i < length; i++) // whitespace! :)
         std::cout << cars[i].printNo();
}

int main()
{
    // ...

    printCarNumbers(&mycars[0], mycars.size());
} 

printCarNumbersは、範囲を示す2つの反復子を受け入れるために、実際には異なる設計にする必要があります。

38
GManNickG

次のように、placement-newを使用できます。

class Car {
    int _no;
public:
    Car( int no ) :_no( no ) {
    }
};

int main() {
    void* raw_memory = operator new[]( NUM_CARS * sizeof( Car ) );
    Car* ptr = static_cast<Car*>( raw_memory );
    for( int i = 0; i < NUM_CARS; ++i ) {
        new( &ptr[i] )Car( i );
    }
    // destruct in inverse order    
    for( int i = NUM_CARS - 1; i >= 0; --i ) {
        ptr[i].~Car();
    }
    operator delete[]( raw_memory );
    return 0;
}

より効果的なC++からの参照-Scott Meyers:
項目4-不要なデフォルトコンストラクターを避ける

59
Chan

ポインターの配列を作成できます。

Car** mycars = new Car*[userInput];
for (int i=0; i<userInput; i++){
    mycars[i] = new Car(...);
}

...

for (int i=0; i<userInput; i++){
    delete mycars[i];
}
delete [] mycars;

または

Car()コンストラクターはパブリックである必要はありません。配列を構築するクラスに静的メソッドを追加します。

static Car* makeArray(int length){
    return new Car[length];
}
18
Squall

いいえ、ありません。 New-expressionは、デフォルトの初期化のみを許可するか、初期化をまったく許可しません。

回避策は、operator new[]を使用して生メモリバッファを割り当て、次に、デフォルト以外のコンストラクタを使用して、placement-newを使用してそのバッファにオブジェクトを構築することです。

4
AnT

良い質問。私は同じ質問をして、それをここで見つけました。本当の答えは、@ Dan-Paradoxです。これを行うための標準的な構文方法はありません。したがって、これらの答えはすべて、問題を回避するためのさまざまな選択肢です。

私は答えを自分で読みましたが、私の個人的な慣習に完璧なものは特に見つかりませんでした。おそらく私が固執するメソッドは、デフォルトのコンストラクターとsetメソッドを使用しています。

 class MyClass 
 {
 int x、y、z; 
 public:
 MyClass():x(0)、y(0) 、z(0) {} 
 MyClass(int _x、int _y、int _z):x(_x)、y(_y)、z(_z){}//単一宣言の場合
 void set(int _x、int _y、int _z)
 {
 x = _x; 
 y = _y; 
 z = _z; 
} 
}; 

標準の初期化コンストラクターはまだ存在するため、複数必要ない場合は通常どおり初期化できますが、そうでない場合は、コンストラクターで初期化されるすべての変数を設定するsetメソッドがあります。したがって、私はこのようなことをすることができます:

 int len = 25; 
 MyClass list = new MyClass [len]; 
 for(int i = 0; i <len; i ++)
 list [i ] .set(1,2,3); 

これは、コードを混乱させることなく正常に機能し、自然に流れます。


これが、初期化する必要のあるオブジェクトの配列をどのように宣言するのか疑問に思う人への私の答えです。

具体的には、車のIDの配列を提供しようとしていますが、これは常に一意である必要があります。上記で説明した私の方法でそれを行うことができ、forループでi+1setメソッドに送信される引数として-しかし、私があなたのコメントで読んだことから、IDをより内部的に開始したいようです。他の誰かがあなたのクラスCarを使用している場合。

これが必要な場合は、静的メンバーを使用できます。

 class Car 
 {
 static int current_id; 
 int id; 
 public:
 Car():id(current_id ++) {} 
 
 int getId(){return id; } 
}; 
 int Car :: current_id = 1; 
 
 ... 
 
 int cars = 10; 
 Car * carlist = new Car [cars]; 
 
 for(int i = 0; i <cars; i ++)
 cout << carlist [i]。 getId()<< ""; // "1 2 3 4 5 6 7 8 9 10" 
を出力します

このように、IDは内部で管理されるため、IDの開始について心配する必要はありません。

4
Codesmith

いつでもポインタの配列を作成し、車のオブジェクトをポイントしてから、必要に応じてforループでオブジェクトを作成し、そのアドレスを配列に保存できます。

#include <iostream>
class Car
{
private:
  Car(){};
  int _no;
public:
  Car(int no)
  {
    _no=no;
  }
  void printNo()
  {
    std::cout<<_no<<std::endl;
  }
};
void printCarNumbers(Car *cars, int length)
{
    for(int i = 0; i<length;i++)
         std::cout<<cars[i].printNo();
}

int main()
{
  int userInput = 10;
  Car **mycars = new Car*[userInput];
  int i;
  for(i=0;i<userInput;i++)
      mycars[i] = new Car(i+1);

新しい方法に注意してください!!!

  printCarNumbers_new(mycars,userInput);


  return 0;
}    

新しいメソッドで変更する必要があるのは、パラメータ内の静的オブジェクトよりもポインタとして車を処理することと、たとえばprintNo()メソッドを呼び出す場合です:

void printCarNumbers_new(Car **cars, int length)
{
    for(int i = 0; i<length;i++)
         std::cout<<cars[i]->printNo();
}

mainの最後で、このように動的に割り当てられたすべてのメモリを削除するとよい

for(i=0;i<userInput;i++)
  delete mycars[i];      //deleting one obgject
delete[] mycars;         //deleting array of objects

応援してくれてありがとう!

3
kondilidisn

C++ 11のstd::vectoremplace_back

  std::vector<Car> mycars;

  for (int i = 0; i < userInput; ++i)
  {
      mycars.emplace_back(i + 1); // pass in Car() constructor arguments
  }

出来上がり!

Car()デフォルトコンストラクターは呼び出されません。

mycarsが範囲外になると、削除が自動的に行われます。

3
rustyx

解決方法の1つは、何らかの理由でコンストラクタをプライベートにしたい場合に、静的ファクトリメソッドを与えて配列を割り当てることです。

static Car*  Car::CreateCarArray(int dimensions)

しかし、なぜ1つのコンストラクターをパブリックにし、他のコンストラクターをプライベートにしているのですか?

しかし、とにかくもう1つの方法は、パブリックコンストラクタをデフォルト値で宣言することです

#define DEFAULT_CAR_INIT 0
Car::Car(int _no=DEFAULT_CAR_INIT);
2
Neera

私はあなたが望むことを行うことができるタイプセーフな方法があるとは思わない。

1
Dagang

私のやり方

Car * cars;

// else were

extern Car * cars;

void main()
{
    // COLORS == id
    cars = new Car[3] {
        Car(BLUE),
            Car(RED),
            Car(GREEN)
    };
}
0
Mitch Pisa

インプレース演算子newを使用できます。これは少し恐ろしいことなので、工場に保管することをお勧めします。

Car* createCars(unsigned number)
{
    if (number == 0 )
        return 0;
    Car* cars = reinterpret_cast<Car*>(new char[sizeof(Car)* number]);
    for(unsigned carId = 0;
        carId != number;
        ++carId)
    {
        new(cars+carId) Car(carId);
    }
    return cars;
}

そして、これで使用される新しいものと一致するように、対応する破棄を定義します。

0
Keith