85 votes

Initialisation de tableau d'objets sans constructeur par défaut

#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;
}    

Je veux créer un tableau de voitures mais je reçois l'erreur suivante :

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

Y a-t-il un moyen de rendre cette initialisation sans rendre le constructeur Car() public ?

79voto

Chan Points 3445

Vous pouvez utiliser le placement-nouveau comme ceci :

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;
}

Référence de C++ plus efficace - Scott Meyers :
Point 4 - Éviter les constructeurs par défaut gratuits

52voto

GManNickG Points 155079

Non.

Mais voilà ! Si vous utilisez std::vector<Car>, comme vous devriez l'être (ne jamais utiliser new[]), alors vous pouvez spécifier exactement comment les éléments doivent être construits*.

*En quelque sorte. Vous pouvez spécifier la valeur de laquelle faire des copies.


Comme ceci :

#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
} 

Avec la fonction supplémentaire :

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());
} 

Note printCarNumbers devrait vraiment être conçu différemment, pour accepter deux itérateurs désignant une plage.

24voto

Squall Points 1106

Vous pouvez créer un tableau de pointeurs.

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;

ou

Car() constructeur n'a pas besoin d'être public. Ajoutez une méthode statique à votre classe qui construit un tableau :

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

7voto

rustyx Points 2722

Dans les %-% + std::vector +11, vous pouvez instancier des éléments sur place en utilisant emplace_back :

  std::vector<Car> mycars;

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

Voila !

Car() constructeur par défaut n'a jamais invoqué.

La suppression se fera automatiquement lorsque mycars sort du champ d'application.

4voto

AndreyT Points 139512

Non, il n'y en a pas. New-expression n'autorise que l'initialisation par défaut ou aucune initialisation du tout.

La solution serait d'allouer un tampon de mémoire brute en utilisant operator new[], puis de construire des objets dans ce tampon en utilisant placement-new avec un constructeur non par défaut.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X