Nieznana mi składnia konstrukcji obiektu. Dlaczego to działa?

0

Ja w zasadzie nie piszę w C++, ale mam projekt w C++/CLI i niechcący użyłem składni, której nie rozumiem. Zobrazuję to może poniższym przykładem:

#include <iostream>
using namespace std;

class A 
{
   char * _name;
public:
   A()
   {
      _name = "default";
      cout << "Constructor of A: default" << endl;
   }
   A(A& other)
   {
       _name = other._name;
       cout << "Copy constructor of A:" << _name << endl;
   }
   A(A&& other) : _name(other._name)
   {
      other._name = nullptr;
      cout << "Move constructor of A:" << _name << endl;
   }
   A(char * name)
   {
      cout << "Constructor of A:" << name << endl;
      _name = name;
   }
   void SayName()
   {
      cout << _name << endl;
   }
   ~A()
   {
      cout << "Destructor of A:" << _name << endl;
   }
};

int main() 
{
   A(a);
   a.SayName();
}

Mam wątpliwość co do zapisu:

A(a);

Spodziewałem się wywołania konstruktora z jednym parametrem i błędu kompilacji, a dostaje właściwy rezultat

Sukces	#stdin #stdout 0s 16064KB
Constructor of A: default
default
Destructor of A:default

Ok czyli jest coś czego nie wiem, ale wygląda ok.

Dziwnie się zrobiło jednak gdy zrobiłem tak:

int main() 
{
   A a(a);
   a.SayName();
}

i wynik jaki dostałem:

Sukces	#stdin #stdout 0s 4236KB
Copy constructor of A:AWAVA��AUATL�%v 
AWAVA��AUATL�%v 
Destructor of A:AWAVA��AUATL�%v

Jak widać wywołał się konstruktor kopiujący, ale nie widać konstruktora, który utworzył obiekt kopiowany.

Czy może mi ktoś wyjaśnić oba te przypadki?

2
Sarrus napisał(a):

Mam wątpliwość co do zapisu:

A(a);

Spodziewałem się wywołania konstruktora z jednym parametrem i błędu kompilacji, a dostaje właściwy rezultat

Sukces	#stdin #stdout 0s 16064KB
Constructor of A: default
default
Destructor of A:default

Ok czyli jest coś czego nie wiem, ale wygląda ok.

a było jeszcze niezdefiniowane, więc kompilator uznał, że to jest definicja zmiennej (a) (a to to samo co a) typu A.

Dziwnie się zrobiło jednak gdy zrobiłem tak:

int main() 
{
   A a(a);
   a.SayName();
}

i wynik jaki dostałem:

Sukces	#stdin #stdout 0s 4236KB
Copy constructor of A:AWAVA��AUATL�%v 
AWAVA��AUATL�%v 
Destructor of A:AWAVA��AUATL�%v

Jak widać wywołał się konstruktor kopiujący, ale nie widać konstruktora, który utworzył obiekt kopiowany.

To jest definicja obiektu a typu A, która ma skopiować sobie zawartośc obiektu a -- który (jak się okazało) istnieje, bo właśnie (w bieżącej instrukcji) został powołany do życia. :)

0

Addendum: podobnie działać będzie (sprawdziłem!):

int(x);
int y(y);
0

To jest definicja obiektu a typu A, która ma skopiować sobie zawartośc obiektu a -- który (jak się okazało) istnieje, bo właśnie (w bieżącej instrukcji) został powołany do życia. :)

To jest najdziwniejsza rzecz jaką widziałem w tym roku. W zeszłym zresztą też.

Wynika z tego, że jestem w stanie utworzyć obiekt z użyciem wyłącznie konstruktora kopiującego. Bosko.

0

To wina tego, że składnia C++ (z winy odziedziczonej składni C) jest mocno pokiełbaszona... Nie da się jej na przykład sparsować gramatyką bezkontekstową...

0

A co takiego dziwnego jest w tej konstrukcji? Nie do końca rozumiem Twoje zdziwienie. Zwróć uwagę że nie jest tu uruchamiany konstruktor kopiujący tylko domyślny. Na marginesie, przypisanie const char * "default" do _name jest nieco "śliskie" a w konstruktorze kopiującym zapomniałeś const.

0
Mokrowski napisał(a):

A co takiego dziwnego jest w tej konstrukcji? Nie do końca rozumiem Twoje zdziwienie. Zwróć uwagę że nie jest tu uruchamiany konstruktor kopiujący tylko domyślny. Na marginesie, przypisanie const char * "default" do _name jest nieco "śliskie" a w konstruktorze kopiującym zapomniałeś const.

Nie wiem o czym piszesz -- w drugiej konstrukcji jest oczywiście wywoływany konstruktor kopiujący -- co widać choćby po komunikatach. Po prostu obiekt a istnieje przed zakończeniem wykonywania konstruktora kopiującego i jest kopiowany sam na siebie...

0

@Mokrowski: Nie jestem przyzwyczajony do C++, więc dla mnie nie jest naturalne. W pierwszym przypadku napisałem:

Ok czyli jest coś czego nie wiem, ale wygląda ok.

Dla mnie jeżeli zrobię tak:

char * name = "name";
auto a = A(name);

to spodziewałem się, że jeżeli usunę przypisanie to zostanie nadal wywołany ten sam konstruktor, a zamiast tego dostałem błąd kompilacji, że brak konstruktora domyślnego.
Inną sprawą jest, że w przypadku, gdy piszemy metodę, a name będzie polem klasy to:

A(name);

wywoła konstruktor domyślny, ale to

A(this->name);

wywoła konstruktor z parametrem.

Nie od dzisiaj mi wiadomo, że deklaracje w C++ są pokręcone i rozumiem dlaczego tak się dzieje, ale nie uważam, że wszystko tutaj jest super cacy. No ale cóż, taki klimat.

Natomiast jeżeli chodzi o drugi przypadek, to uważam, że czegoś takiego kompilator nie powinien dopuszczać.

0

@koszalek-opalek już zasadniczo odpowiedział, ale dorzucę jeszcze swoje trzy grosze.

Sama zasada działania takiej definicji/deklaracji jest imo bardzo nieintuicyjna, czego nie omieszkałem zablogować: https://dev.krzaq.cc/post/code-doodles-5-quite-surprising-parse/

Inicjalizacja zmiennej samą sobą to UB.

Całego problemu możesz uniknąć stosując uniform initialization.

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