Kolejność deklaracji składowych w klasie

0

W jakiej kolejności deklarujecie składowe klasy aby zachować przejrzystość kodu? Chodzi mi o to aby rozgraniczyć na konstruktory, destruktory, metody, metody wirtualne, struktury, zmienne oraz składowe publiczne, chronione i prywatne? Czy tak jak poniżej jest ok:


class MyClass {
private:
    struct MyStruct1 {};
protected:
    struct MyStruct2 {};
public:
    struct MyStruct3 {};
private:
    MyStruct1 st1;
    int var1;
protected:
    MyStruct1 st2;
    int var2;
public:
    MyStruct1 st3;
    int var3;
public:
    MyClass();
    MyClass(int);
    ~MyClass();
private:
    void method1();
    virtual void method4() = 0;
protected:
    void method2();
    virtual void method5() = 0;
public:
    void method3();
    virtual void method6() = 0;

};
0

Tak, żeby był ładny alignment :P

2

Cały post to opinia.

Jak dla mnie takie spagetti jest szalenie nieczytelne. Osobiście preferuję po prostu podział na sekcje public (góra), protected (środek) i private (dół). Jeśli są jakieś dodatkowe (np. public slots w Qt), to są pod daną sekcją, czyli public slots by było między public a protected.

Wyjątkiem jest celowe ustawienie jakichś alignmentów/kolejności elementów, ale to się raczej zdarza na niskim poziomie przy POD-ach, gdzie wszystko jest public i tak.

0

@kq Ale wówczas pod poszczególnymi modyfikatorami w jaki sposób porządkujesz składowe? Mógłbyś proszę podać jakiś przykład? Za każdym razem kiedy tworzę klasę mam problem jak posegregować zmienne aby zachować porządek i nie mogę wypracować jakiegoś przejrzystego stylu.

0

Pierw funkcje, potem zmienne. Jeśli definiuję enumy/typy to są one na górze, o ile nie dotyczą wyłącznie jednej funkcji/zmiennej, wtedy są blisko niej.

Przy czym nie pamiętam żebym w ciągu ostatnich lat miał obiekt w public/protected, tylko funkcje. W sumie protected prawie nie używam również.

1

Ja stosuję taki zapis klas

class SomeClass
{
public:
    SomeClass();//najpierw domyślny konstruktor...
    SomeClass(int parameter);//potem reszta konstruktorów
    ~SomeClass();//na końcu destruktor
//jeden \n odstępu
   int penSize;//składniki publiczne, jeśli są, grupowane względem powiązania logicznego
   QColor penColor;
//jeden \n odstępu pomiędzy poszczególnymi grupami logicznymi
   int margin;
   int border
//jeden \n odstępu separujący składniki od metod
   void setupPen();//które też są pogrupowane według wspólnej logiki/zagadnienia jakiego dotyczą

   void setupViewportBox(int margin, int border);//nastawianie obramowania viewportu nie jest logicznie powiązane z inicjacją pióra, stąd odstęp pomiędzy metodami

//powyższe przewija się też w sekcjach protected i private
protected:
private:
};
1

Moje preferencje:

class JakasKlasa
    : public Rodzic1 // najpierw klasy zwykłe
    , public Rodzic2
    , public Interface1 // potem interfejsy
    , public Interface2
    , public std::enable_shared_from_this<JakasKlasa> // jeśli jest to ostatnia klasa w hierarchii i zarządzana jest przez std::shared_ptr
// moje ulubione "udziwnienie" widać powyżej, przecinki i dwukropek są na początku a nie na końcu linii!
// zalety są dwie:
// po dopisaniu na końcu nowego dziedziczenia nie ma konfliktów w merge albo są do naprawienia jednym klikiem
// optycznie widać lepiej sekwencję dziedziczenia
// tak samo wygląda u mnie lista inicjalizacyjna - podobne zalety ale się częściej objawiają
{
public:
      struct Struct //typy wewnętrzne
      {
      };

      using JakisTyp = std::function<void(int, const std::string&);

public:
      ~JakasKlasa(); // preferuje deklarację destrura jako pierwszą i piszę ją zawsze nawet jak jest pusty, żeby przyspieszyć proces budowania

      JakasKlasa();
      explicit JakasKlasa(int x); // domyślnie dopisuje explicit potem się zastanawiam, czy nie lepiej usunąć

      void publiczneMeotody();

public: // metody statyczne też wolę pod własnym "public"
     static std::shared_ptr<Interface1> makeSome();

public: /* Interface1 */ // celowe powtórnie public
     void metodaZInterfejsu1();

public: /* Interface2 */
     void metodaZInterfejsu2();
     void metoda2ZInterfejsu2();

protected:
     void metodaChroniona()

private:
     void meodaPrywatna();

private:
     int pole; // wszystkie pola na samym dole, grupki oddzielone pustymi liniami jeśli to potrzebne.
};

Przy czym:

  • pola tylko prywatne (chyba, że jest to "value object"/"typ strukturalny," który nigdy nie ma metod wirtualnych)
  • nigdy nie używam przyjaźni, zawsze mi ją ładnie rozwiązuje jakaś pomocnicza metoda publiczna.
  • definicje operatorów zawsze są jednolinijkowymi inline'ami wywołującymi jakąś metodę publiczną, np:
    inline MojTyp operator+(const MojTyp& b) const
    {
      return add(b);
    }

I jeszcze oczywiście coś takiego: https://4programmers.net/Forum/C_i_C++/137960-C++_Unikanie_include_w_plikach_naglowkowych?p=513176#id513176

0
MarekR22 napisał(a):
class JakasKlasa
   : public Rodzic1
   , public Rodzic2
   , public Interface1
   , public Interface2

moje ulubione "udziwnienie" widać powyżej, przecinki i dwukropek są na początku a nie na końcu linii!

To też najbardziej sensowny sposób formatowania listy inicjalizacyjnej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0