Inicjalizacja tablicy obiektów bez bezparametrowego konstruktora?

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.

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

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

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..
}
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)

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