Kilka pytań na przeciazenia operatorow i konwersji typow

0

Witam. Mam kilka pytań dotyczących przeciążenia operatorów i konwersji typów. Może są one banalne, ale wasza odpowiedź może mi naprawdę pomóc w dalszej nauce.

  1. Co obsługuje ten fragment kodu:
class Counter
{ 
...
}
Counter i;
Counter a=i; //chodzi o ta linijke

operator przypisania (=) czy konstruktor kopiujący?

2.Chciałbym wiedzieć jak wygląda krok po kroku ten proces:

class Counter
{
int itsVal;
public:
Counter():itsVal(0) {}
Counter(int x):itsVal(x) {}
~Counter;
}
int main()
{
Counter i;
i=10;// chodzi o ta linijke
return 0;
}

Wydaje mi ze zachodzi konwersja poprzez wywołanie konstruktora z jednym argumentem typu int, który tworzy tymczasowy obiekt a następnie ten tymczasowy obiekt jest przypisywany obiektowi i. Dobrze to rozumiem?

0

sprawdź sobie. wyświetlaj co się dzieje, np:

Counter():itsVal(0) { cout<<"konstruktor bezparametrowy"<<endl; }
0

Ad. 2
Tak, w tym wypadku tak jest, a najłatwiej byłoby to sprawdzić po prostu wrzucając jakiegoś cout'a do konstruktora.
Wygląda to mniej więcej tak:
najpierw jest próba dopasowania operatora przypisania, jeżeli odpowiedni operator się nie znajdzie, to wtedy są poszukiwania odpowiedniego konstruktora kopiującego.

0

A co w takiej sytuacji:

#include <iostream>
using namespace std;

class Counter
{
    public:
    Counter():itsVal(0) {}
    Counter(const Counter& rhs) {cout<<"kopiujacy\n";}
    Counter(int x):itsVal(x) {cout<<"1-argument\n"; }
    Counter(int x,int z):itsVal(x),itsVal2(z) {cout<<"2-argumenty\n";}
    ~Counter() {}
    int GetVal() const {return itsVal;}
    void SetVal(int x) {itsVal=x;}
    int itsVal;
    int itsVal2;
};
int main()
{
    Counter i(1,2);
    cout<<i.itsVal<<endl;
    cout<<i.itsVal2<<endl;
    i=10;
    cout<<i.itsVal<<endl;
    cout<<i.itsVal2<<endl;
    return 0;
}

Wynik:

2-argumenty
1
2
1-argument
10
4273318

Jak widać zmienna itsVal2 równie uległa zmianie. Jak temu zapobiec?

1

Oczywiście, że ulega zmianie. Powód jest prosty i wypadałoby, żebyś dobrze go znał (bo inaczej doświadczysz 'dziwnych' zachowań):
w momencie przypisania i = 10; najpierw jest tworzony obiekt na podstawia konstruktora Counter(int x), a później ten tymczasowy obiekt jest przypisywany do obiektu i.
Jeżeli nie masz zdefiniowanego operatora przypisania(w tym wypadku przyjmującego jako argument typ Counter) to następuje kopiowanie wartość po wartości(ogólnie bajt w bajt).

W wielu przypadkach takie zachowanie przypisania jest ok, ale jak już będziesz miał w klasie jakieś wskaźniki i do tego alokacja pamięci będzie się odbywać w konstruktorze, a zwalnianie w destruktorze, to program dość szybko Ci się sypnie przez odwoływanie się do zwolnionej, albo zwalnianie już zwolnionej pamięci. Skopiowany będzie wskaźnik, a nie to na co on pokazuje.

Rozwiązaniem w tym wypadku jest przeciążenie operatora = przyjmującego jako argument obiekt klasy Counter, albo int'a.

Ogólnie ten temat nie jest taki prosty, bo wypadałoby jeszcze wiedzieć kiedy zajdzie niejawna konwersja między typami podstawowymi itd.
Np. co się odpali przy przypisaniu Obiekt i; i = 2; jeżeli Obiekt ma zdefiniowany operator przypisania przyjmujący float i konstruktor przyjmujący int?
Jest z tym trochę zabawy, ale przyda się na przyszłość - pobaw się, potestuj, zobacz jak to się zachowuje.

0

Ok wiem już kiedy odpali konstruktor a kiedy operator. Na przyszłość gdyby ktoś potrzebował:

Class Counter {...};
Counter i;
Counter j=i;

Obsłuży domyślny konstruktor kopiujący, chyba że sami go zdefiniowaliśmy.

Counter i=10;

Obsłuży konstruktor przyjmujący jako jedyny argument wartość int.

i=j;

Obsłuży operator przypisania przyjmujący jako argument typ Counter(zazwyczaj jest to domyślny operator chyba że podobnie jak z konstruktorem kopiującym zdefiniowaliśmy go sami).

i=10;

Obsłuży operator przypisania przyjmujący jako argument wartość int(taki operator musimy napisać sami), jeśli taki operator nie istnieje polecenie to zostanie obsłużone przez konstruktor przyjmujący jako argument wartość int.

Jeśli myśle źle proszę o jak najszybszą poprawę.

Mam jeszcze jedno pytanie: Jak obsłużyć polecenie typu:

class Counter{...};
Counter i;
int Val=i;
0
class Counter
  {
   public:
   operator int() const {cout<<"konwersja na int"<<endl; return itsVal;} // Counter obj; int i=obj;
   explicit
   Counter():itsVal(0),itsVal2(0) {cout<<"domyslny"<<endl;}
   explicit
   Counter(const Counter& rhs):itsVal(rhs.itsVal),itsVal2(rhs.itsVal2) {cout<<"kopiujacy"<<endl;}
   Counter(int x):itsVal(x),itsVal2(0) {cout<<"1-argument"<<endl;}
   Counter(int x,int z):itsVal(x),itsVal2(z) {cout<<"2-argumenty"<<endl;}
   Counter &operator=(const Counter& rhs) {itsVal=rhs.itsVal; itsVal2=rhs.itsVal2; cout<<"przypisanie"<<endl; return *this;}
   Counter &operator=(int x) {itsVal=x; itsVal2=0; cout<<"przypisanie int"<<endl; return *this;}
   ~Counter() {cout<<"destruktor"<<endl;}
   int GetVal() const {return itsVal;}
   void SetVal(int x) {itsVal=x;}
   int itsVal;
   int itsVal2;
  };

Counter obj=5; // użyje konstruktor "1-argument\n"; chyba że ten konstruktor został poprzedzony słówkiem explicit wtedy - błąd kompilatora.

0

Czyli ogólnie konwersja z typu zdefiniowanego przez programistę na typ wbudowany wygląda:

operator Typ_konwersji() const {return (Typ_konwersi) zwracana_składowa;}

tak?

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