Konstruktory i operatory przypisania

0

Napisałem klasę z przeładowanymi konstruktorami i operatorami przypisania, czy mogę ją jakoś ulepszyć?

  1. Czy są sytuacje kiedy nie pisze się Example& operator= tylko Example operator=?
  2. Czy zawsze trzeba pisać instrukcję if (this != &ex)?
class Example
{
private:
	size_t size;
	int *tab;
public:
	Example() = default;
	Example(size_t size) : size(size), tab(new int[size])
	{
		std::cout << "Constructor!" << std::endl;
		std::iota(tab, tab + size, 1);
	}
	~Example()
	{
		std::cout << "Destructor!" << std::endl;
		delete[] tab;
	}
	Example(const Example& ex)
	{
		std::cout << "Copy Constructor!" << std::endl;
		size = ex.size;
		tab = new int[size];
		std::copy(ex.tab, ex.tab + ex.size, tab);
	}
	Example& operator=(const Example& ex)
	{
		std::cout << "Copy Assignment Operator!" << std::endl;
		if (this != &ex)
		{
			delete[] tab;
			size = ex.size;
			tab = new int[size];
			std::copy(ex.tab, ex.tab + ex.size, tab);
		}
		return *this;
	}
	Example(Example&& ex)
	{
		std::cout << "Move Constructor!" << std::endl;
		if (this != &ex)
		{
			size = ex.size;
			tab = new int[size];
			std::copy(ex.tab, ex.tab + ex.size, tab);
			delete[] ex.tab;
			ex.size = 0;
			ex.tab = nullptr;
		}
	}
	Example& operator=(Example&& ex)
	{
		std::cout << "Move Assignment Operator!" << std::endl;
		if (this != &ex)
		{
			size = ex.size;
			tab = new int[size];
			std::copy(ex.tab, ex.tab + ex.size, tab);
			delete[] ex.tab;
			ex.size = 0;
			ex.tab = nullptr;
		}
		return *this;
	}
};

2

Example& operator= oznacza, że próbujesz zmodyfikować bieżący obiekt.

Example operator= oznacza, ze próbujesz stworzyć nowy obiekt, a więc nowy obiekt powinien wlecieć w miejsce startego, czyli znów doszłoby do przypisania czyli to by przybrało taką rekurencyjną postać.

Ponadto mało widocznym elementem zapisu Example& operator= jest pominięcie logiki jaka może być użyta w konstruktorze.

if (this != &ex) Alternatywnei możesz napisać if (this == &ex) i wtedy zwrócić *this :D Generalnie chodzi o to, byś nie robił dodatkowo zbędnej alokacji.

czy mogę ją jakoś ulepszyć - czymkolwiek jest klasa Example to mógłbyś uniknąć całego zamieszania z int * tab stosując szablon vector.

2

Programowałem w C++ 9 lat temu :D

Kilka poprawek do tego co napisałem:

  1. masz rację Example operator= tworzy obiekt, który potem znika, ale najłatwiej to sobie uzymysłowisz gdy przekształcisz zapis:
Example a(3)
Example b(5)
Example c(2)
c = a = b;

Wtedy działania a = b stworzy tymczasowy obiekt i będzie probowało go przypisać do c. Część c = ... uruchomi ponownie operator przypisania, a tymczasowy obiekt zostanie usunięty, oczywiście to wyrażenie wygeneruje drugi tymczasowy obiekt, który już nikt nie spożytkuje.

Gdybyś zapisał Example & operator= wtedy o 2 kopie byłoby mniej.

Nie wiem jak to u Ciebie działa, ale konstruktor z słowem default rozumiem, że tam pojawi się domyślny konstruktor, który nic nie robi, a więc tab, oraz size nie bedą ustawione. Praca z takim domyślnym obiektem powinna skończyć się błedami w trakcie gdy będziesz probował zwolnić z niego pamięć.

1

@leto: Przenoszenie(konstruktor i przypisanie) masz skopane. Skopiuj wskaźnik bez żadnych alokacji.

1

@leto, zamiast robić dwie wersje operatora =, zrób tak:

    Example& operator=(Example ex)
    {
        std::swap(tab, ex.tab);
        std::swap(size, ex.size);
        return *this;
    }

I popraw konstruktor przenoszący:

Example(Example&& ex) 
: size(ex.size), tab (ex.tab)
{
    ex.tab = nullptr;
    ex.size = 0;
}
0

@tajny_agent

	Example& operator=(Example&& ex)
	{
		std::cout << "Move Assignment Operator!" << std::endl;
		if (this != &ex)
		{
			size = ex.size;
			ex.size = 0;
			tab = ex.tab;
			ex.tab = nullptr;
		}
		return *this;
	}
	Example(Example&& ex) 
		: size(ex.size), tab(ex.tab)
	{
		std::cout << "Move Constructor!" << std::endl;
		ex.size = 0;
		ex.tab = nullptr;
	}

Tak będzie prawidłowo?
Co do if (this != &ex), nie muszę tego pisać przy konstruktorach, bo one zawsze są puste?
A przy operatorach przypisania powinienem?

1

W konstruktorach nie dajesz if, bo nie ma możliwości, by ex był tym samym obiektem co this (obiekt jest w trakcie tworzenia)

0

Co do swapa, czytałem że to niezbyt w duchu C++, bo odwleka destrukcję starego obiektu wprowadzając niedeterminizm (albo coś bliskiego niedeterminizmu).

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