Czy powinniśmy dodawać konstruktory do struktur?

14

Często używamy struktur c ++ do definiowania struktury danych w przeciwieństwie do klasy, która może być kompletnym modułem z metodami składowymi. W głębi duszy wiemy, że oba są takie same (luźno mówiąc).

Fakt, że często używamy / traktujemy struktury jako jednostki tylko danych, powoduje, że nie dodajemy również domyślnych konstruktorów. Ale konstruktory są zawsze świetne, upraszczają i pomagają eliminować błędy.

Czy byłoby rozczarowanie, gdyby dodać domyślne konstruktory do moich struktur danych?

Czy implementacja domyślnego konstruktora powoduje, że struct Non-POD (zwykły stary typ danych), pod warunkiem spełnienia innych kryteriów?

Patrząc z perspektywy, rozważmy prosty przykład, ale w rzeczywistości struktura byłaby znacznie większa.

struct method
{
    char    name[32];
    float   temperature;
    int     duration;
};

Za każdym razem, gdy tworzę metodę, muszę się martwić (delikatnie mówiąc), czy zapomniałem ustawić jakąś wartość. Wyobraź sobie, że zapomniałem ustawić temperaturei zastosować metodę do systemu, który ma teraz losowo wysoką wartość i powoduje chaos. Lub zapomniałem ustawić, durationa teraz metoda stosuje się przez nieznany długi czas.

Dlaczego powinienem brać odpowiedzialność za inicjalizację obiektu za każdym razem zamiast implementować jego konstruktor, który to gwarantuje?

zadane
źródło
Jeśli musisz wymusić, że dozwolone są tylko niektóre wartości, nie masz zwyczajnie starego typu danych. Jeśli chcesz tylko wygodnych sposobów inicjowania struktur, zrobią to zwykłe stare funkcje.
Doval
To zależy od tego, co robią ci konstruktorzy. Myślę, że posiadanie konstruktora na prostej strukturze jest całkowicie uzasadnione, jeśli tylko ustawia wartości pola w podstawowy sposób.
Gort the Robot
@Doval to nie jest pytanie, zaktualizowałem post. Steven: tak, konstruktorzy będą tylko przypisywać wartości domyślne.
zadane
@StevenBurnap: Jeśli konstruktor robi coś więcej niż tylko ustawianie wartości pól w podstawowy sposób, bardziej odpowiednie jest to mieć. Nawet na strukturze.
Jan Hudec
2
Chodzi mi o to, że jeśli zaczniesz znajdować skomplikowaną logikę w konstruktorze, prawdopodobnie powinieneś przekształcić ją w klasę. (IMHO) Ale to naprawdę tylko kwestia stylu jak tylko rzeczywistej różnicy structi classjest to, że domyślnie prywatny, a drugi do publicznej wiadomości.
Gort the Robot

Odpowiedzi:

13

Czasami właściwe jest dodanie konstruktora do struktury, a czasem nie.

Dodanie konstruktora (dowolnego konstruktora) do struktury uniemożliwia użycie na nim inicjatora agregującego. Więc jeśli dodasz domyślny konstruktor, będziesz musiał również zdefiniować inny niż domyślny konstruktor inicjujący wartości. Ale jeśli chcesz mieć pewność, że zawsze inicjujesz wszystkich członków, jest to właściwe.

Dodanie konstruktora (ponownie dowolnego konstruktora) powoduje, że nie jest on POD, ale w C ++ 11 większość reguł, które wcześniej stosowano tylko do POD, zmieniono tak, aby dotyczyły standardowych obiektów układu, a dodawanie konstruktorów tego nie łamie. Tak więc inicjator agregacji jest w zasadzie jedyną rzeczą, którą tracisz. Ale często jest to również duża strata.

Jan Hudec
źródło
8

Z C ++ 11 możesz to zrobić

struct method
{
    char    name[32] {};
    float   temperature = 42.141521;
    int     duration = -6;
};

A gdy zapomnisz coś zainicjować, otrzymasz domyślną inicjalizację.

Vorac
źródło
-1

Szybka odpowiedź:

To zależy od tego, co chcesz osiągnąć.

Długa, rozszerzona, nudna odpowiedź:

Uderzyłeś w gwóźdź.

Zwykle nie podoba mi się, że „C ++” pozwala „Struct (s)” pozwala deklarować metody. Najlepiej używam wyraźnych „klas (y)” dla wymaganych metod i POD „struktur (y)” tylko dla pól.

Zgadzam się jednak, że niektóre podstawowe proste operacje, takie jak:

  • przypisz wartości początkowe („konstruktor”)
  • zrób kopię struktury („konstruktor kopii)
  • przypisać wartości do istniejącej struktury („operator przeciążenia przypisuje”)

Są wymagane, a w tych okolicznościach metody konstrukcji mają sens.

Sugestia

Innym potencjalnym rozwiązaniem jest użycie struktur POD, ale nadal koncepcyjnie traktuj je jako klasy i obiekty.

Zawiń te deklaracje w przestrzeń nazw i dodaj funkcje globalne dla najważniejszych akcji.

Deklaracja kodu może być podobna do tej:

namespace Customers
{
  struct CustomerStruct
  {
    char[255] FirstName;
    char[255] LastName;
    int Age;
    bool IsAlive;
    bool IsMarried;
  }; // struct

  CustomerStruct* CreateCustomer
  (
    char* NewFirstName;
    char* NewLastName;
    int NewAge;
    bool NewIsAlive;
    bool NewIsMarried;
  )
  {
    CustomerStruct* NewCustomer = new CustomerStruct();
      NewCustomer->FirstName = NewFirstName;
      NewCustomer->LastName = NewLastName;
      NewCustomer->Age = NewAge;
      NewCustomer->IsAlive = NewIsAlive;
      NewCustomer->IsMarried = NewIsMarried;
    return NewCustomer;
  } // CustomerStruct* CreateCustomer (...)

} // namespace

Kod, który stosuje rozwiązanie, może wyglądać mniej więcej tak:

#include <Customers>

using Customers;

int main (...)
{
   int ErrorCode = 0;

   CustomerClass* ThisCustomer =
     Customers::CreateCustomer
      ("John", "Doe", 23, true, true);

   // do something with "ThisCustomer"

   delete ThisCustomer;

   return ErrorCode;
} // int main(...)

To alternatywne podejście jest lepsze, gdy wymagany jest ogromny przydział pamięci danych lub interakcja z innymi bibliotekami współdzielonymi niskiego poziomu.

To podejście, z pewnymi zmianami, jest stosowane w rozwoju gier.

Dodatkowy

Osobiście uważam rozszerzenie składni dla „C ++”, a nawet nowe PL oparte na „C ++”, które rozwiązuje ten problem:

// "Plain Old Data" Structure
// No Methods, No "Functors", allowed
strict struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;
}; // strict struct

// Object Oriented "Plain Old Data" Structure
// Yes, Methods and "Functors" allowed
relaxed struct CustomerStruct
{
  char[255] FirstName;
  char[255] LastName;
  int Age;
  bool IsAlive;
  bool IsMarried;

  public void Foo();
  public void Bar();

  public (void*) (SomeFunctor) ();
}; // relaxed struct

// Class and Object Oriented
class CustomerClass
{
  public char[255] FirstName;
  public char[255] LastName;
  public int Age;
  public bool IsAlive;
  public bool IsMarried;

  public void Foo();
  public void Bar();
}; // class

Twoje zdrowie.

umlcat
źródło