Jak "zwrócić obiekt" w C++?
Wiem, że tytuł brzmi znajomo, ponieważ jest wiele podobnych pytań, ale proszę o inny aspekt problemu (znam różnicę między posiadaniem rzeczy na stosie a umieszczaniem ich na stosie).
W Javie zawsze mogę zwrócić odniesienia do obiektów "lokalnych"
public Thing calculateThing() {
Thing thing = new Thing();
// do calculations and modify thing
return thing;
}
W C++, aby zrobić coś podobnego mam 2 opcje
(1) Mogę używać referencji, gdy chcę "zwrócić" obiekt
void calculateThing(Thing& thing) {
// do calculations and modify thing
}
Następnie użyj go jak to
Thing thing;
calculateThing(thing);
(2) lub Mogę zwrócić wskaźnik do dynamicznie przydzielanego obiektu
Thing* calculateThing() {
Thing* thing(new Thing());
// do calculations and modify thing
return thing;
}
Następnie użyj go w ten sposób
Thing* thing = calculateThing();
delete thing;
Przy pierwszym podejściu Nie będę musiał zwalniać pamięci ręcznie, ale dla mnie utrudnia to odczytanie kodu. Problem z drugim podejściem polega na tym, że muszę pamiętać o delete thing;
, co nie wygląda zbyt ładnie. Nie chcę zwracać skopiowanej wartości, ponieważ jest nieefektywna( myślę), więc tutaj przychodzą pytania
- czy jest trzecia rozwiązanie (które nie wymaga kopiowania wartości)?
- czy jest jakiś problem, jeśli będę trzymać się pierwszego rozwiązania?
- kiedy i dlaczego powinienem użyć drugiego roztworu?
8 answers
Udowodnij to.Nie chcę zwracać skopiowanej wartości, ponieważ jest nieefektywna
Poszukaj RVO i NRVO, a w C++0x move-semantyka. W większości przypadków w C++03 parametr out jest po prostu dobrym sposobem, aby Twój kod był brzydki, a w C++0x faktycznie ranisz się używając parametru out.
Po prostu napisz czysty kod, zwracany przez wartość. Jeśli wydajność jest problemem, profiluj go (przestań zgadywać) i znajdź, co możesz zrobić, aby go naprawić. Prawdopodobnie nie wróci rzeczy z funkcji.
To powiedziawszy, jeśli jesteś martwy na pisanie w ten sposób, prawdopodobnie chcesz zrobić parametr out. Pozwala to uniknąć dynamicznej alokacji pamięci, co jest bezpieczniejsze i na ogół szybsze. Wymaga to, abyś miał jakiś sposób na skonstruowanie obiektu przed wywołaniem funkcji, co nie zawsze ma sens dla wszystkich obiektów.
Jeśli chcesz użyć alokacji dynamicznej, możesz przynajmniej umieścić ją w inteligentnym wskaźniku. (I tak powinno się to robić cały czas) Wtedy nie martw się o kasowanie czegokolwiek, rzeczy są bezpieczne dla WYJĄTKÓW itp. Jedynym problemem jest to, że jest prawdopodobnie wolniejszy niż zwracanie przez wartość i tak!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 06:27:20
Po prostu utwórz obiekt i zwróć go
Thing calculateThing() {
Thing thing;
// do calculations and modify thing
return thing;
}
Myślę, że zrobisz sobie przysługę, jeśli zapomnisz o optymalizacji i po prostu napiszesz czytelny kod (będziesz musiał później uruchomić profiler - ale nie Pre-optymalizacji).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-29 07:54:27
Po prostu zwróć taki obiekt:
Thing calculateThing()
{
Thing thing();
// do calculations and modify thing
return thing;
}
Spowoduje to wywołanie konstruktora kopiującego, więc możesz chcieć wykonać własną implementację tego. TAK:
Thing(const Thing& aThing) {}
To może działać trochę wolniej, ale może nie być problemem w ogóle.
Update
Kompilator prawdopodobnie zoptymalizuje wywołanie konstruktora kopiującego, więc nie będzie żadnych dodatkowych kosztów. (Jak zauważył dreamlax w komentarzu).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-23 09:57:41
Czy próbowałeś użyć inteligentnych wskaźników (jeśli coś jest naprawdę dużym i ciężkim obiektem), jak auto_ptr:
std::auto_ptr<Thing> calculateThing()
{
std::auto_ptr<Thing> thing(new Thing);
// .. some calculations
return thing;
}
// ...
{
std::auto_ptr<Thing> thing = calculateThing();
// working with thing
// auto_ptr frees thing
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 06:33:06
Jednym z szybkich sposobów na określenie, czy Konstruktor kopiujący jest wywoływany, jest dodanie logowania do konstruktora kopiującego twojej klasy:
MyClass::MyClass(const MyClass &other)
{
std::cout << "Copy constructor was called" << std::endl;
}
MyClass someFunction()
{
MyClass dummy;
return dummy;
}
Wywołanie someFunction
; liczba linii "Copy constructor was called", które otrzymasz, będzie się wahać między 0, 1 i 2. Jeśli nie otrzymasz żadnej, Twój kompilator zoptymalizował wartość zwracaną (co jest dozwolone). Jeśli otrzymasz don ' t get 0, a Twój Konstruktor kopiujący jest śmiesznie drogi, to poszukaj alternatywnych sposobów zwracania instancji z twoje funkcje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 06:35:33
Po pierwsze masz błąd w kodzie, masz na myśli Thing *thing(new Thing());
i tylko return thing;
.
- Użyj
shared_ptr<Thing>
. Niech to będzie wskazówka. Zostanie ona usunięta, gdy ostatnie odniesienie doThing
wyjdzie poza zakres. - pierwsze rozwiązanie jest bardzo powszechne w naiwnych bibliotekach. Ma pewną wydajność i składniowe koszty, unikaj go, jeśli to możliwe
- użyj drugiego rozwiązania tylko wtedy, gdy możesz zagwarantować, że żadne wyjątki nie zostaną rzucone, lub gdy wydajność jest absolutnie krytyczne (będziesz łączyć się z C lub assembly, zanim to stanie się istotne).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 06:30:38
To może nie być prawda. Kompilatory mogą dokonać optymalizacji, aby zapobiec kopiowaniu.Nie chcę zwracać skopiowanej wartości, ponieważ jest nieefektywna
Na przykład GCC dokonuje tej optymalizacji. W poniższym programie nie są wywoływane ani move constructor, ani copy constructor, ponieważ kopiowanie ani przenoszenie nie jest wykonywane. Zwróć również uwagę na adres c
. Nawet jeśli obiekt c
jest utworzony wewnątrz funkcji f()
, c
znajduje się w ramce stosu main()
.
class C {
public:
int c = 5;
C() {}
C(const C& c) {
cout << "Copy constructor " << endl;
}
C(const C&& c) noexcept {
cout << "Move Constructor" << endl;
}
};
C f() {
int beforeC;
C c;
int afterC;
cout << &beforeC << endl; //0x7ffee02f26ac
cout << &c << endl; //0x7ffee02f2710 (notice: even though c is instantiated inside f(), c resides in the stack frame of main()
cout << &afterC << endl; //0x7ffee02f26a8
return c;
}
C g() {
C c = f(); ///neither copy constructor nor move constructor of C are called, since none is done
cout << &c << endl; //0x7ffee02f2710
return c;
}
int main() {
int beforeC;
C c = g(); ///neither copy constructor nor move constructor of C are called, since none is done
int afterC;
cout << &beforeC << endl; //0x7ffee02f2718
cout << &c << endl; //0x7ffee02f2710 (notice:even though c is returned from f,it resides in the stack frame of main)
cout << &afterC << endl; //0x7ffee02f270c
return 0;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-10 06:18:31
Jestem pewien, że ekspert C++ przyjdzie z lepszą odpowiedzią, ale osobiście podoba mi się drugie podejście. Używanie inteligentnych wskaźników pomaga w problemie zapominania o delete
i jak mówisz, wygląda to czystsze niż tworzenie obiektu przed ręką (i nadal konieczność usunięcia go, jeśli chcesz przydzielić go na stercie).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 06:30:01