Inicjalizacja tablicy obiektów bez bezparametrowego konstruktora?

Odpowiedz Nowy wątek
2019-05-29 19:05
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.

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ć??

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, 2019-05-29 19:27

Pozostało 580 znaków

2019-05-30 09:29
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.

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 .

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

    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, 2019-05-30 16:38

Pozostało 580 znaków

2019-05-30 13:24
DRK
0

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

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

Pozostało 580 znaków

2019-05-30 15:13
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).

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

Pozostało 580 znaków

2019-05-30 15:46
0

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

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.
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ć. - Bartłomiej Golenko 2019-05-30 16:09
A jaką wersję C++ masz dostępną? Możesz używać std::array? - MarekR22 2019-05-30 16:13
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. - Bartłomiej Golenko 2019-05-30 16:27
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 2019-05-30 16:29

Pozostało 580 znaków

2019-05-30 17:16
0

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

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, 2019-05-31 08:42

Pozostało 580 znaków

2019-05-30 20:19
0

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

#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
no napisałem mu wyżej szablon, który to robi. - MarekR22 2019-05-31 08:40
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" :) - Mokrowski 2019-05-31 12:46
bo szablon pisze się raz i używa 100 razy. - MarekR22 2019-05-31 19:05
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). - Mokrowski 2019-05-31 19:19
to wszystko jest zrobione powyżej (tyle że z wyjątkami). Jeszcze dorobić constexpr i jest wypas. - MarekR22 2019-06-01 00:38

Pozostało 580 znaków

2019-05-31 18:21
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, 2019-05-31 18:22
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. - Bartłomiej Golenko 2019-05-31 21:46

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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