Inicjalizacja tablicy obiektów bez bezparametrowego konstruktora?

Inicjalizacja tablicy obiektów bez bezparametrowego konstruktora?
KM
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 4 lata
  • Postów:473
0

MojaKlasa nie posiada bezparametrowego konstruktora, bo jest on bez sensu - taki konstruktor musiałby stworzyć nieużywalną instancję. Żeby obiektu MojejKlasy dało się użyć, konstruktor musi otrzymać więcej danych.

Ale teraz chciałbym mieć tablicę obiektów tego typu.

Kopiuj
MojaKlasa tab[100] = {
    MojaKlasa(3, 4),
    MojaKlasa(3, 5),
    MojaKlasa(3, 6),
    MojaKlasa(3, 7),
    MojaKlasa(4, 4),
    MojaKlasa(4, 6),
    // ...
}

Za dużo pisania, 100 obiektów tak ręcznie wypisać??

Kopiuj
MojaKlasa tab[100];
for(size_t i = 0; i < 100; i++)
    tab[i] = MojaKlasa(3 + i/4, 4 + (i/4+1)*(i%4));

Oczywiście się nie kompiluje, gdyż MojaKlasa nie posiada bezparametrowego konstruktora.

Widzę 2 wyjścia, żadne mi się nie podoba:

  1. Dać MojejKlasie bezparametrowego konstruktora, który po prostu jest, ale nic nie robi, w szczególności nie tworzy sensownego obiektu; kolejne wady: nagle MojaKlasa nie może mieć const pól bo inaczej nie działa przypisanie tab[i] = MojaKlasa(cośtam);
  2. Zaalokować dynamicznie przestrzeń o rozmiarze 100*sizeof(MojaKlasa) zamiast pisać MojaKlasa tab[100], a potem pętla jak wyżej - no ale jeśli da się uniknąć dynamicznej alokacji to chyba należy.

Czy jest jakieś 3-cie wyjście, którego nie widzę?

PS. Tak, to musi być tablica - nie mogę zrobić std::vector - na Arduino nie ma biblioteki standardowej.

edytowany 3x, ostatnio: kmph
TomaszLiMoon
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 11 godzin
  • Postów:530
0

Moim zdaniem najlepszym wyjściem w tej sytuacji jest dodanie konstruktora domyślny MojaKlasa() = default
Zmienne mutable pozwalają na zmianę w stałych obiektach poprzez funkcje set.

Kopiuj
class MojaKlasa
{

 public:

    MojaKlasa() = default ;
    MojaKlasa( int x , int y ): x{x} , y{y} {}

    void set( int _x , int _y ) const { x=_x; y=_y; }

 private:

    mutable int x {0};
    mutable int y {0};
};


int main()
{

    const MojaKlasa tab[100];

    for(size_t i = 0; i < 100; i++) tab[i].set(3 + i/4, 4 + (i/4+1)*(i%4));

    return 0;
}

Jeżeli potrzebne są stałe pola to najprostszym rozwiązanie jest użycie wskaźników .

Kopiuj
    MojaKlasa *tab[100];   // MojaKlasa zawiera stałe pola 

    for( size_t i = 0; i < 100; i++ )
    {
        tab[i] = new MojaKlasa(3 + i/4, 4 + (i/4+1)*(i%4));
    }
    
    delete []tab;

lub można użyć unique_ptr<>

Kopiuj
    unique_ptr<MojaKlasa> tab[100]; // MojaKlasa zawiera stałe pola 

    for( size_t i = 0; i < 100; i++ )
    {
        tab[i] = make_unique<MojaKlasa>(3 + i/4, 4 + (i/4+1)*(i%4));
    }

A jeżeli chodzi o STL to zobacz https://www.arduinolibraries.info/libraries/arduino-stl

edytowany 1x, ostatnio: TomaszLiMoon
DR
  • Rejestracja:prawie 16 lat
  • Ostatnio:12 miesięcy
0

Jeśli te 2 parametry są inicjowane wartościami wg wzoru powyżej, to można tak:

Kopiuj
class MojaKlasa
{
public:
  MojaKlasa():
    x{3 + n / 4},
    y{4 + (n / 4 + 1) * (n % 4)}
  {
    ++n;
  }
  static int n;
  int x;
  int y;
};

int MojaKlasa::n{0};
BG
  • Rejestracja:prawie 6 lat
  • Ostatnio:około 9 godzin
  • Postów:288
0

DISCLAIMER: absolutnie i w żadnym wypadku nie uważam poniższego za dobry pomysł ;)
Ale bez STL-a i innych nowych wynalazków (lambdy w tym przypadku tylko z lenistwa - możesz użyć zwykłych funkcji).

Kopiuj
#include <iostream>


class MojaKlasa
{
    const int a;
    const int b;

    static int defaultInitA(int i) { return i; }
    static int defaultInitB(int i) { return 2*i; }

    static int (*initA)(int);
    static int (*initB)(int);
    static int idx ;

public:
    MojaKlasa();
    static void configureGenerator(int start, int (*iA)(int), int (*iB)(int)) { idx=start; initA = iA; initB = iB; }

};

int MojaKlasa::idx;
int (* MojaKlasa::initA)(int) = MojaKlasa::defaultInitA;
int (* MojaKlasa::initB)(int) = MojaKlasa::defaultInitB;

MojaKlasa::MojaKlasa() : a(initA(idx)), b(initB(idx))
{
    std::cout << a << " " << b << std::endl;
    ++idx;
}

int main()
{
    MojaKlasa tab1[5];
    std::cout << std::endl;

    // "rekonfiguracja generatora" obiektów...
    MojaKlasa::configureGenerator(0,
        [] (int i) -> int { return 3 + i/4; },
        [] (int i) -> int { return 4 + (i/4+1)*(i%4); }
    );

    MojaKlasa tab2[5];
}
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
0

To jest C++ a nie C, więc najprościej użyć std::vector

Kopiuj
std::vector<MojaKlasa> tab;
tab.reserve(100);
for(size_t i = 0; i < 100; i++)
    tab.emplace_back(3 + i/4, 4 + (i/4+1)*(i%4));

Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
BG
Oczywiście że najprościej - jeśli możesz użyć STL-a. Ale nawet jeśli jest jakaś implementacja dla Arduino, to niekoniecznie zdecydowałbym się jej użyć.
MarekR22
A jaką wersję C++ masz dostępną? Możesz używać std::array?
BG
To już pytanie do @kmph. Problemem nie jest wersja C++ tylko czasem bardzo limitowane zasoby procesora. Nie mam pojęcia ile zajmie ArduinoSTL jeśli użyjemy z niego tylko i wyłącznie std::vector, ale na małych Atmegach czasem każdy bajt się liczy.
MarekR22
tak wiem, że Arduino nie ma dużo zasobów. Ale C++11 ma constexpr (w wyższych wersjach łatwiejsze w użyciu), które potrafi zaoszczędzić zasobów.
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
0

Mała lekka (bez sterty) namiastka std::vector

Kopiuj
template<typename T, size_t max>
class LocalVector {
public:
    ~LocalVector() {
        clear();
    }

    T* data() {
        return reinterpret_cast<T*>(mBuffer);
    }

    const T* data() const {
        return reinterpret_cast<const T*>(mBuffer);
    }

    template<typename ...Args>
    void emplace_back(Args&&...args)
    {
        throwIfCantAppend();
        new (data() + mCount)T(std::forward<Args>(args)...);
        ++mCount;
    }

    void push_back(T&& x)
    {
        throwIfCantAppend();
        new (data() + mCount)T(std::forward<T>(x));
        ++mCount;
    }

    void push_back(const T& x)
    {
        throwIfCantAppend();
        new (data() + mCount)T(x);
        ++mCount;
    }

    void clear() noexcept {
         while (mCount != 0) {
              (data() + mCount - 1)->~T();
              --mCount;
         }
    }

    size_t size() const { return mCount; }

    T& operator[](size_t index) {
         return data()[index];
    }

    const T& operator[](size_t index) const {
         return data()[index];
    }

    T* begin() { return data(); }
    T* end() { return data() + mCount; }

    const T* begin() const { return data(); }
    const T* end() const { return data() + mCount; }

    const T* cbegin() const { return data(); }
    const T* cend() const { return data() + mCount; }

    size_t capacity() const { return max; }

    bool empty() const { return mCount == 0; }
    bool full() const { return mCount >= max; }

private:
    void throwIfCantAppend() {
         if (mCount >= max) throw std::out_of_range("LocalVector");
    }    
private:
    size_t mCount = 0;
    char mBuffer[sizeof(T[max])];
};

https://wandbox.org/permlink/DgM72CI8CWpa9uHq


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 2x, ostatnio: MarekR22
MO
  • Rejestracja:prawie 10 lat
  • Ostatnio:około godziny
  • Lokalizacja:Tam gdzie jest (centy)metro...
0

Albo pytanie jest bardziej skomplikowane niż mi się wydaje, albo wystarczy zwykłe rzutowanie i konstrukcja w miejscu klasy.
Ot trochę "edukacyjnie"...

Kopiuj
#include <iostream>
#include <cstddef>

namespace {

constexpr std::size_t SIZE = 100;

}

class MyClass {
public:
    MyClass(int a, int b)
      : a{a}, b{b}, z{a * b} {}
    // nadmiarowo dla rozwiania wątpliwości stawianych minimalnych wymagań
    MyClass(const MyClass&) = delete;
    MyClass& operator = (const MyClass&) = delete;
    MyClass(MyClass&&) = delete;
    MyClass& operator = (MyClass&&) = delete;

    void show() const {
        std::cout << a << ' ' << b << ' ' << z << '\n';
    }
private:
    int a;
    int b;
    const int z;
};

int main() {
    char buffer[sizeof(MyClass[SIZE])];
    MyClass * table = reinterpret_cast<MyClass*>(buffer);
    // konstrukcja w miejscu
    for(std::size_t i = 0; i < SIZE; ++i) {
        new (&(table[i])) MyClass(3 + i / 4, 4 + (i / 4 + 1) * (i % 4));
    }
    for(auto i = 0U; i < SIZE; ++i) {
        table[i].show();
    }
    // TODO: ew. destrukcja elementów tablicy..
}

Każdy problem w informatyce można rozwiązać, dodając kolejny poziom pośredniości,z wyjątkiem problemu zbyt dużej liczby warstw pośredniości — David J. Wheeler
MarekR22
no napisałem mu wyżej szablon, który to robi.
MO
No tak, ale po co mu szablon jak to są (w zasadzie) 3 linijki kodu a w nich potencjalne użycie "new in place"? Rzecz jasna produkcyjnie reinterpret_cast bym nie dopuścił bo to "zuło jest" :)
MarekR22
bo szablon pisze się raz i używa 100 razy.
MO
No to jak by to zrobić porządnie (jak sugerujesz), z uwzględnieniem że to MCU i embedded, to raczej sensowniej zrobić LimitedArray czyli kontener z maksymalną dopuszczalną wielkością, brakiem wyjątków, brakiem alokacji dynamicznej. To po prostu inny problem niż występujące tu alokowanie konstrukcji obiektu w wyznaczonej pamięci. Poza tym w C++ free-standing, nie ma pełnej biblioteki standardowej a w implementacji dla AVR i GCC, w zasadzie zostaje wyłącznie core języka (no i to co daje C free-standing) ba, nie ma nawet new (choć dla Arduino jest).
MarekR22
to wszystko jest zrobione powyżej (tyle że z wyjątkami). Jeszcze dorobić constexpr i jest wypas.
KM
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 4 lata
  • Postów:473
0
TomaszLiMoon napisał(a):

A jeżeli chodzi o STL to zobacz https://www.arduinolibraries.info/libraries/arduino-stl

MarekR22 napisał(a):

Mała lekka (bez sterty) namiastka std::vector

Ciekawe.

Zastanawiam się tylko, czemu biblioteki standardowej nie ma tam fabrycznie. Czy mieli jakiś dobry powód, by jej nie dawać?

Bo jeśli tak, to... jakoś mam opory przed zarówno korzystaniem z czyichś portów jak i przed implementowaniem elementów biblioteki standardowej samodzielnie, przynajmniej dopóki nie będę wiedział, czemu jej nie ma. Być może to nieracjonalny strach.

MarekR22 napisał(a):

A jaką wersję C++ masz dostępną? Możesz używać std::array?

Array też nie ma. C++ można chyba sobie skonfigurować nawet na wersję 17, tyle że nie ma biblioteki standardowej.

TomaszLiMoon napisał(a):

Zmienne mutable pozwalają na zmianę w stałych obiektach poprzez funkcje set
(...)
Jeżeli potrzebne są stałe pola

No pozwalają, ale... czy to nie jest nadużycie mutable? W sensie - stałe pola nie są potrzebne, żeby coś napisać, one nie zwiększają siły wyrazu języka, one tylko pozwalają dołożyć jakąś wartstwę statycznej weryfikacji programu. Korzystanie z const a potem walenie mutable jakby mija się z celem, już lepiej w ogóle olać całą tą const correctness.

DRK napisał(a):

Jeśli te 2 parametry są inicjowane wartościami wg wzoru powyżej, to można tak:

Bartłomiej Golenko napisał(a):

DISCLAIMER: absolutnie i w żadnym wypadku nie uważam poniższego za dobry pomysł ;)
Ale bez STL-a i innych nowych wynalazków (lambdy w tym przypadku tylko z lenistwa - możesz użyć zwykłych funkcji).

Fajne wykorzystanie staticów, nie wpadłem na to :)

W zasadzie mogę to nawet wykorzystać, program jest tylko po to, by sz.p. Laborant mnie zaliczył, raczej nie grozi mi zachodzenie za 2 lata w głowę co ja tam narobiłem :)

Mokrowski napisał(a):

Albo pytanie jest bardziej skomplikowane niż mi się wydaje, albo wystarczy zwykłe rzutowanie i konstrukcja w miejscu klasy.
Ot trochę "edukacyjnie"...

Wow. Placement new.

Uczciwie przyznaję, że dotąd unikałem placement new jak ognia. Cytując oficjalne FAQ:

ADVICE: Don’t use this “placement new” syntax unless you have to. Use it only when you really care that an object is placed at a particular location in memory. For example, when your hardware has a memory-mapped I/O timer device, and you want to place a Clock object at that memory location.

DANGER: You are taking sole responsibility that the pointer you pass to the “placement new” operator points to a region of memory that is big enough and is properly aligned for the object type that you’re creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn’t properly aligned, you can have a serious disaster on your hands (if you don’t know what “alignment” means, please don’t use the placement new syntax). You have been warned.

Co do mnie dotarło po przeczytaniu tego:

Albo masz sporo niskopoziomowej, ezoterycznej wiedzy albo co tu w ogóle robisz??

Ponieważ nie wiem dokładnie, jakie obiekty mają jakie wymogi odnośnie alignment ani od czego to zależy, to...

WYDAJE MI SIĘ, że tablice char mają zagwarantowane, że są wyalignmentowane do dowolnego "rozsądnego" obiektu; ale tylko WYDAJE MI SIĘ, a tutaj chyba "wydaje mi się" to za mało.

Wierzę Ci, że zrobiłeś to OK, ja tylko tłumaczę się, czemu sam się w to nie bawiłem (ani na to nie wpadłem)

edytowany 1x, ostatnio: kmph
BG
Nie wiem jaki masz tam procesor - Twój pierwszy pomysł (czyli ręczne zadeklarowanie każdego obiektu) może się w praktyce okazać jedynym działającym (bo chyba przy tym podejściu dasz radę przekonać linker żeby dane obiektów wrzucił do flasha a nie do RAM). Ale tu już nie mam wystarczająco dużo doświadczenia z arduino.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)