Operator[] a przypisanie

Operator[] a przypisanie
NX
  • Rejestracja:ponad 20 lat
  • Ostatnio:około 13 lat
0

Witajcie!
Piszę sobie pewną klasę, w której występują dynamiczne tablice dwuwymiarowe. Wszystko fajnie, tylko, że nie bardzo wiem jak mogę napisać operator indeksowania i przypisania jednocześnie?
chodzi mi o to, abym mógł wykonać coś takiego:

Kopiuj
wsk[1][2]=0;

chyba nie mogę skorzystać po prostu z operator[], bo ona przyjmuje tylko jeden argument?
W klasie na razie nic nie ma oprócz konstruktorów i operatora wypisania:

Kopiuj
class matrix
{
        double **wsk;
        unsigned int k,w;
        public:
        matrix(unsigned int c, unsigned int j);
        ~matrix();
        void randomize(unsigned int zakres);
        friend ostream& operator<<(ostream&, const matrix&);
};
Endrju
  • Rejestracja:około 22 lata
  • Ostatnio:ponad rok
1
  1. Lepiej zrób to za pomocą operatora (). (Może mieć dowolną ilość argumentów)
  2. Żeby móc modyfikować zwracaj referencję.

C++ FAQ [13.10] How do I create a subscript operator for a Matrix class?


"(...) otherwise, the behavior is undefined".
edytowany 1x, ostatnio: Endrju
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
0

Powinien wystarczyć operator[], niech zwróci double* np. return wsk[index];


Zobacz pozostałe 2 komentarze
MJ
Musi zwrócić referencję, bo jak zwróci referencję do wskaźnika to przypisuje, do elementu wsk[cos][0]. A tak jak napisałeś, żeby wskaźnik zwracał sam, to nawet nie powinno zadziałać z tego co pamiętam jak pisałem kiedyś program.
byku_guzio
Zauważ, że tu mamy dynamiczną tablicę dwuwymiarową!
MJ
Ok porerało mi się, że gdybyśmy mieli tablicę wskaźników na jakąś inną klasę
byku_guzio
w takim wypadku zwrócenie wskaźnika też by działało, ale lepsze, bezpieczniejsze byłoby zwrócenie referencji
MJ
No właśnie nie działa, sprawdź ten przykład: https://ideone.com/HcPO4
NX
  • Rejestracja:ponad 20 lat
  • Ostatnio:około 13 lat
0
byku_guzio napisał(a)

Powinien wystarczyć operator[], niech zwróci double* np. return wsk[index];

Chyba nie, albo ja coś popitoliłem:
double* matrix::operator[](unsigned int el)
{
return wsk[el];
}
Wywala to segfaulta;

hauleth
Zwracaj referencję nie wskaźnik.
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
0

To samo z siebie seg'a nie wywali, pokaż jak tego używasz i czy wcześniej alokujesz pamięć dla wsk?

Coś takiego mam na myśli:

Kopiuj
class Foo
{
private:
	double **wsk;

public:
	Foo()
	{
		wsk = new double*[10];
		for(int i = 0; i < 10; i++)
		{
			wsk[i] = new double[10];
			for(int j = 0; j < 10; j++)
				wsk[i][j] = i*10+j;
		}
	}

	~Foo()
	{
		for(int i = 0; i < 10; i++)
			delete [] wsk[i];

		delete [] wsk;
	}

	double* operator[](size_t index)
	{
		return wsk[index];
	}
};
 
int main ()
{
	Foo f;

	cout << f[2][3] << endl;
	f[2][3] = 25.03;
	cout << f[2][3] << endl;

	return 0;
}

edytowany 2x, ostatnio: byku_guzio
NX
  • Rejestracja:ponad 20 lat
  • Ostatnio:około 13 lat
0

Powiem ci, że chyba mamy identycznie, a u mnie mimo to jest coś nie tak :|

Kopiuj
class matrix
{
        double **wsk;
        unsigned int k,w;
        double randDouble(double low, double high);
        public:
        matrix(unsigned int c, unsigned int j);
        ~matrix();
        void randomize(unsigned int zakres);
        friend ostream& operator<<(ostream&, const matrix&);
        double* operator[](unsigned int el);
        matrix &operator=(const matrix&);

};

matrix::matrix(unsigned int c, unsigned int j)
{
    wsk=new double*[c];
    for (unsigned int i=0;i<c;i++)
        wsk[i]=new double[j];
    k=c-1;
    w=j-1;
}

matrix::~matrix()
{
    for(unsigned int i=0;i<w;i++)
        delete [] wsk[i];
    delete [] wsk;
}

void matrix::randomize(unsigned int zakres)
{
    srand(time(NULL));
    for(unsigned int i=0;i<=k;i++)
        for(unsigned int j=0;j<=w;j++)
            wsk[i][j]=1.0/(rand()%100);
}

ostream& operator << (ostream& o, const matrix& s)
{
    unsigned int k=s.k;
    unsigned int w=s.w;
    o<<"+";
    for(unsigned int i=0;i<=(k*(PRECISION+2)+8+k);i++)
        o<<"-";
    o<<"+"<<endl;

    o<<setprecision(PRECISION)<<setfill('0')<<fixed;

    for(unsigned int i=0;i<=k;i++)
    {
        o<<"| ";
        for(unsigned int j=0;j<=w;j++)
            o<<s.wsk[i][j]<<" ";
        o<<"|"<<endl;
    }
    o<<"+";
    for(unsigned int i=0;i<=(k*(PRECISION+2)+8+k);i++)
        o<<"-";
    o<<"+";

}

/*matrix &matrix::operator= (const matrix &s)
{
    for(unsigned int i=0;i<k;i++)
        for(unsigned int j=0;j<w;j++)
            wsk[i][j]=s.wsk[i][j];
}*/

double* matrix::operator[](unsigned int el)
{
    return wsk[el];
}

int main()
{
    cout << "Hello world!" << endl;
    matrix a(5,5);
    a.randomize(99);
    cout<<a;
    return 0;
}
 
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
0

No nie mamy identycznie. Choćby dlatego, że operatora [] w ogóle nie używasz :p

Nie wiem czym to kompilujesz, ale przykre, że to się Ci skompilowało. Masz błąd w operatorze << przez co dostajesz ten segf zapomniałeś zwrócić wartość, w tym wypadku return o;
Jeżeli dobrze kojarzę to nie zwrócenie wartości z funkcji, która deklaruje, że coś zwraca objawia się undefined behaviour, czyli może się stać cokolwiek.

Jeszcze jedno: masz błąd w destruktorze, zwalniasz o jedną tablicę za mało. W for powinno być i<=w. W ogóle ten pomysł z k i w mniejszymi o jeden od faktycznej ilości elementów jest bez sensu - zmniejsza czytelność, trzeba pamiętać w pętlach, że ma być <=, no i więcej pisania.


edytowany 1x, ostatnio: byku_guzio
NX
  • Rejestracja:ponad 20 lat
  • Ostatnio:około 13 lat
0

Tak, faktycznie wysłałem ci wersję "działającą" czyli akurat bez [] ;-) Po prostu zapomniałem dopisać.

Dzięki ci Dobry człowieku za wskazówki :-)
Faktycznie z tym ostream dałem ciała ;-/

Tylko kurczę chyba muszę pogrzebać w ustawieniach IDE Code::Blocks.
Bo coś mi tu nie pasuje. Normalnie jakbym kompilował poza IDE, to g++ wysypałoby ostrzeżenie, o tym, że wychodzę z funkcji nie zwracając niczego.
A kompilując w Code::Blocks nie widzę w ogóle ostrzeżeń. Dziwne. Muszę najwidoczniej gdzieś mieć wyłączone pokazywanie ostrzeżeń

edytowany 1x, ostatnio: NeoX
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0

przydałby się jeszcze konstruktor kopiujący i operator kopiowania-przypisania. w najprostszym przypadku można ich po prostu zabronić:

Kopiuj
private:
	Foo(const Foo&);
	Foo& operator=(const Foo&);
NX
  • Rejestracja:ponad 20 lat
  • Ostatnio:około 13 lat
0
Azarien napisał(a)

przydałby się jeszcze konstruktor kopiujący i operator kopiowania-przypisania. w najprostszym przypadku można ich po prostu zabronić:

Kopiuj
private:
	Foo(const Foo&);
	Foo& operator=(const Foo&);

Jakie są konsekwencje nie zabronienia?

byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
0

Zostaną wygenerowane automatycznie i nie będą się zachowywać tak jakbyś tego chciał - robią płytką kopię.


edytowany 1x, ostatnio: byku_guzio
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
1

Jakie są konsekwencje nie zabronienia?

Kopiuj
Foo raz;
if (true)
{
    Foo dwa = raz;
}
raz[1][1] = 1; // jebut!
BL
  • Rejestracja:ponad 16 lat
  • Ostatnio:5 miesięcy
0

Dlaczego miało by mu to wywalić, skoro konstruktor kopiujący kopiuje wszystkie składniki obiektu?


Zapraszam osoby początkujące na indywidualne szkolenia z programowania w JAVA. Dla najlepszych, po zakończonym kursie, praca ze stawką początkową 10-12tyś netto. Kurs trwa od 6 do 24 miesięcy, 1-2h w tygodniu, 150PLN/h. szkolenia kropka java malpaa gmail kropka com
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
1

Przyczyna jest bardzo prosta. Skopiują się wskaźniki, odpali destruktor. Później odwołuje się do już zwolnionej pamięci.


BL
  • Rejestracja:ponad 16 lat
  • Ostatnio:5 miesięcy
0

Czyli konstruktor kopiujący nie kopiuje zawartości, tylko adresy pamięci? Trochę to chyba nie rozsądne


Zapraszam osoby początkujące na indywidualne szkolenia z programowania w JAVA. Dla najlepszych, po zakończonym kursie, praca ze stawką początkową 10-12tyś netto. Kurs trwa od 6 do 24 miesięcy, 1-2h w tygodniu, 150PLN/h. szkolenia kropka java malpaa gmail kropka com
iooi
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 11 lat
  • Postów:573
0

Kopiuje zawartość. Tylko że tą zawartością mogą być wskaźniki.

edytowany 1x, ostatnio: iooi
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 7 lat
1

@Blood: jak niby kompilator ma się domyśleć co chcesz zrobić? W jaki sposób skopiować obiekt? Co tak naprawdę ma być kopiowane, a co wyliczane? Chcesz głęboką czy płytką kopię? I co ewentualnie jest pod wskaźnikami w klasie?

Dlatego właśnie domyślny konstruktor kopiujący/operator przypisania zachowuje się logicznie - kopiuje zawartość klasy. To na co pokazują wskaźniki już nie jest jej zawartością(patrząc pod kątem języka).


BL
  • Rejestracja:ponad 16 lat
  • Ostatnio:5 miesięcy
0

Okej okej, rozumiem.


Zapraszam osoby początkujące na indywidualne szkolenia z programowania w JAVA. Dla najlepszych, po zakończonym kursie, praca ze stawką początkową 10-12tyś netto. Kurs trwa od 6 do 24 miesięcy, 1-2h w tygodniu, 150PLN/h. szkolenia kropka java malpaa gmail kropka com
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0

w C++11 żeby było trudniej dodano jeszcze konstruktor „przesuwający” (move constructor) i odpowiadający mu operator przypisania.
ich zadaniem jest skopiowanie właśnie samych wskaźników, i wy-null-owanie ich w drugim obiekcie.

edytowany 1x, ostatnio: Azarien
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

Zrób tak jak biali ludzie to robią - z funkcją at().

Kopiuj
class MojaWspanialaDwuwymiarowaTablicaDouble {
public:
  double &at(unsigned int x, unsigned int y);
  const double &at(unsigned int x, unsigned int y) const;
}

//MojaWspanialaDwuwymiarowaTablicaDouble m;
m.at(12,3) = 123;

http://www.cplusplus.com/reference/stl/vector/at/

edytowany 3x, ostatnio: vpiotr
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)