String einlesen lassen (Klassen)
-
@Wade1234 tu was @wob gesagt hat.
-
@Swordfish habe ich. operator= wird aufgerufen und laut http://www.cplusplus.com/reference/string/string/operator=/ wird der inhalt kopiert.
-
@Wade1234 sagte in String einlesen lassen (Klassen):
@Swordfish habe ich. operator= wird aufgerufen und laut http://www.cplusplus.com/reference/string/string/operator=/ wird der inhalt kopiert.
Ich verstehe nicht, was das mit dem Thema "getter und setter", die deiner Meinung nach alles langsam machen, zu tun haben sollte.
-
@wob sagte in String einlesen lassen (Klassen):
@Wade1234 sagte in String einlesen lassen (Klassen):
@Swordfish habe ich. operator= wird aufgerufen und laut http://www.cplusplus.com/reference/string/string/operator=/ wird der inhalt kopiert.
Ich verstehe nicht, was das mit dem Thema "getter und setter", die deiner Meinung nach alles langsam machen, zu tun haben sollte.
naja dass da im gegensatz zum direkten schreiben ein weiterer kopiervorgang stattfindet.
-
@Wade1234 Code? Link zu godbolt?
-
@Wade1234 sagte in String einlesen lassen (Klassen):
naja dass da im gegensatz zum direkten schreiben ein weiterer kopiervorgang stattfindet.
Aber nicht, so wie @Finnegan schon geschrieben hat, wenn man
const &
Getter und Setter benutzt.
-
@Swordfish sagte in String einlesen lassen (Klassen):
@Wade1234 Code? Link zu godbolt?
direktzugriff:
// Type your code here, or load an example. #include <iostream> class MyClass { public: std::string mystring; }; int main() { MyClass myclass; std::cin >> myclass.mystring; return 0; }
MyClass::MyClass() [base object constructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor] nop leave ret MyClass::~MyClass() [base object destructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] nop leave ret main: push rbp mov rbp, rsp push rbx sub rsp, 40 lea rax, [rbp-48] mov rdi, rax call MyClass::MyClass() [complete object constructor] lea rax, [rbp-48] mov rsi, rax mov edi, OFFSET FLAT:_ZSt3cin call std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) mov ebx, 0 lea rax, [rbp-48] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov eax, ebx jmp .L7 mov rbx, rax lea rax, [rbp-48] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov rax, rbx mov rdi, rax call _Unwind_Resume .L7: add rsp, 40 pop rbx pop rbp ret __static_initialization_and_destruction_0(int, int): push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR [rbp-4], edi mov DWORD PTR [rbp-8], esi cmp DWORD PTR [rbp-4], 1 jne .L10 cmp DWORD PTR [rbp-8], 65535 jne .L10 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev call __cxa_atexit .L10: nop leave ret _GLOBAL__sub_I_main: push rbp mov rbp, rsp mov esi, 65535 mov edi, 1 call __static_initialization_and_destruction_0(int, int) pop rbp ret
zugriff über einfachen setter (nebenbei erwähnt fast doppelt so lang):
// Type your code here, or load an example. #include <iostream> class MyClass { std::string mystring; public: void SetMystring(std::string str); }; void MyClass::SetMystring(std::string str) { mystring = str; } int main() { MyClass myclass; std::string str; std::cin >> str; myclass.SetMystring(str); return 0; }
MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >): push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov QWORD PTR [rbp-16], rsi mov rax, QWORD PTR [rbp-8] mov rdx, QWORD PTR [rbp-16] mov rsi, rdx mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) nop leave ret MyClass::MyClass() [base object constructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor] nop leave ret MyClass::~MyClass() [base object destructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] nop leave ret main: push rbp mov rbp, rsp push rbx sub rsp, 104 lea rax, [rbp-80] mov rdi, rax call MyClass::MyClass() [complete object constructor] lea rax, [rbp-112] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor] lea rax, [rbp-112] mov rsi, rax mov edi, OFFSET FLAT:_ZSt3cin call std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) lea rdx, [rbp-112] lea rax, [rbp-48] mov rsi, rdx mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) lea rdx, [rbp-48] lea rax, [rbp-80] mov rsi, rdx mov rdi, rax call MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) lea rax, [rbp-48] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] mov ebx, 0 lea rax, [rbp-112] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] lea rax, [rbp-80] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov eax, ebx jmp .L10 mov rbx, rax lea rax, [rbp-48] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] jmp .L7 mov rbx, rax .L7: lea rax, [rbp-112] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] lea rax, [rbp-80] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov rax, rbx mov rdi, rax call _Unwind_Resume .L10: add rsp, 104 pop rbx pop rbp ret __static_initialization_and_destruction_0(int, int): push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR [rbp-4], edi mov DWORD PTR [rbp-8], esi cmp DWORD PTR [rbp-4], 1 jne .L13 cmp DWORD PTR [rbp-8], 65535 jne .L13 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev call __cxa_atexit .L13: nop leave ret _GLOBAL__sub_I_MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >): push rbp mov rbp, rsp mov esi, 65535 mov edi, 1 call __static_initialization_and_destruction_0(int, int) pop rbp ret
setter mit referenz:
// Type your code here, or load an example. #include <iostream> class MyClass { std::string mystring; public: void SetMystring(const std::string &str); }; void MyClass::SetMystring(const std::string &str) { mystring = str; } int main() { MyClass myclass; std::string str; std::cin >> str; myclass.SetMystring(str); return 0; }
MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&): push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov QWORD PTR [rbp-16], rsi mov rax, QWORD PTR [rbp-8] mov rdx, QWORD PTR [rbp-16] mov rsi, rdx mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) nop leave ret MyClass::MyClass() [base object constructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor] nop leave ret MyClass::~MyClass() [base object destructor]: push rbp mov rbp, rsp sub rsp, 16 mov QWORD PTR [rbp-8], rdi mov rax, QWORD PTR [rbp-8] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] nop leave ret main: push rbp mov rbp, rsp push rbx sub rsp, 72 lea rax, [rbp-48] mov rdi, rax call MyClass::MyClass() [complete object constructor] lea rax, [rbp-80] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor] lea rax, [rbp-80] mov rsi, rax mov edi, OFFSET FLAT:_ZSt3cin call std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) lea rdx, [rbp-80] lea rax, [rbp-48] mov rsi, rdx mov rdi, rax call MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) mov ebx, 0 lea rax, [rbp-80] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] lea rax, [rbp-48] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov eax, ebx jmp .L8 mov rbx, rax lea rax, [rbp-80] mov rdi, rax call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor] lea rax, [rbp-48] mov rdi, rax call MyClass::~MyClass() [complete object destructor] mov rax, rbx mov rdi, rax call _Unwind_Resume .L8: add rsp, 72 pop rbx pop rbp ret __static_initialization_and_destruction_0(int, int): push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR [rbp-4], edi mov DWORD PTR [rbp-8], esi cmp DWORD PTR [rbp-4], 1 jne .L11 cmp DWORD PTR [rbp-8], 65535 jne .L11 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev call __cxa_atexit .L11: nop leave ret _GLOBAL__sub_I_MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&): push rbp mov rbp, rsp mov esi, 65535 mov edi, 1 call __static_initialization_and_destruction_0(int, int) pop rbp ret
der kopiervorgang findet übrigens in zeile 11 statt.
-
Mir scheint der Vergleich nicht ganz fair. @Wade1234 im Direktzugriff schreibst du direkt aus dem Stream in den entsprechenden String während du im Setter Fall einen weiteren String nimmst um die Eingabe zu verarbeiten.
In der Regel wird man irgendwo her schon ein String haben, denn man ablegen will.
Und ob man:void example(std::string str) { MyClass mclass; mclass.mystring = str; }
hat, oder
void example(std::string str) { MyClass mclass; mclass.setMystring(str); }
macht bei korrekter Implementierung vom Setter keinen Unterschied.
-
@Schlangenmensch aber im ausgangspost ging es darum, dass der benutzer einen string eingibt und dieser in der klasse bzw. der instanz abgelegt werden muss.
-
@Wade1234
Und beim nächsten mal braucht er die Klasse in einem anderen Zusammenhang und dann hat er den "Salat".Wenn es um aus einem Stream geht, könnte man auch so was machen:
#include <iostream> class myClass { public: void fromStream(std::istream& str) { str >> myString; } private: std::string myString; }; int main() { myClass myclass; myclass.fromStream(std::cin); return 0; }
-
@Th69 sagte in String einlesen lassen (Klassen):
Aber nicht, so wie @Finnegan schon geschrieben hat, wenn man
const &
Getter und Setter benutzt.Das würde ich gerne nochmal etwas qualifizieren. Ich habe geschrieben:
const&-Getter und Setter, die unnötige Kopien vermeiden, z.B. by value-Parameter + Move
Das ist vielleicht etwas missverständlich formuliert, ich meine hier, dass für Funktionen, die wie eben ein Setter ein Objekt "konsumieren", die Übergabe by value mit anschließendem Move aus diesem Parameter keine schlechte Wahl ist.
Der Code in
SetMystring
im 2. Fall mit "einfachem Setter" sollte also stattdessenmystring = std::move(str);
lauten, wodurch eine überflüssige Kopie vermieden wird. Übergibt man den eingelesenen String ebenfalls via Move mitmyclass.SetMystring(std::move(str));
, oder hat man es z.B. direkt mit einem String-Literal zu tun, so spart man sich eine weitere Kopie, indem man sich den Move-Konstruktor vonstd::string
zunutze macht. Mit einemconst&
-Parameter wäre das nicht möglich, da man von einemconst
-Objekt nicht moven kann.Zugegeben, mit direktem Zugriff kann man sich auch noch das Move sparen, wenn man den String direkt in den Member einliest. Der letztendliche Overhead ist aber nicht so groß, wie das hier dargestellt wird.
@Wade1234 Waren bei dem Godbolt-Code Optimierungen aktiv? Da sind nämlich auffällig viele
call
-Instruktionen drin, von denen ich bereits mit-O2
nicht so viele erwarten würde.
-
@Finnegan sagte in String einlesen lassen (Klassen):
@Wade1234 Waren bei dem Godbolt-Code Optimierungen aktiv? Da sind nämlich auffällig viele
call
-Instruktionen drin, von denen ich bereits mit-O2
nicht so viele erwarten würde.nein da wird dann alles in die main gepackt und statt dessen assign aufgerufen:
MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&): jmp std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) main: push r12 mov edi, OFFSET FLAT:_ZSt3cin push rbp push rbx sub rsp, 64 lea rbx, [rsp+16] lea rbp, [rsp+48] mov QWORD PTR [rsp+8], 0 lea rsi, [rsp+32] mov QWORD PTR [rsp], rbx mov BYTE PTR [rsp+16], 0 mov QWORD PTR [rsp+32], rbp mov QWORD PTR [rsp+40], 0 mov BYTE PTR [rsp+48], 0 call std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) lea rsi, [rsp+32] mov rdi, rsp call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) mov rdi, QWORD PTR [rsp+32] cmp rdi, rbp je .L4 call operator delete(void*) .L4: mov rdi, QWORD PTR [rsp] cmp rdi, rbx je .L10 call operator delete(void*) .L10: add rsp, 64 xor eax, eax pop rbx pop rbp pop r12 ret mov r12, rax jmp .L6 main.cold: .L6: mov rdi, QWORD PTR [rsp+32] cmp rdi, rbp je .L7 call operator delete(void*) .L7: mov rdi, QWORD PTR [rsp] cmp rdi, rbx je .L8 call operator delete(void*) .L8: mov rdi, r12 call _Unwind_Resume _GLOBAL__sub_I_MyClass::SetMystring(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&): sub rsp, 8 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev add rsp, 8 jmp __cxa_atexit
aber direkter zugriff ist trotzdem schneller:
main: push rbp mov edi, OFFSET FLAT:_ZSt3cin push rbx sub rsp, 40 lea rbx, [rsp+16] mov rsi, rsp mov BYTE PTR [rsp+16], 0 mov QWORD PTR [rsp], rbx mov QWORD PTR [rsp+8], 0 call std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) mov rdi, QWORD PTR [rsp] cmp rdi, rbx je .L6 call operator delete(void*) .L6: add rsp, 40 xor eax, eax pop rbx pop rbp ret mov rbp, rax jmp .L3 main.cold: .L3: mov rdi, QWORD PTR [rsp] cmp rdi, rbx je .L4 call operator delete(void*) .L4: mov rdi, rbp call _Unwind_Resume _GLOBAL__sub_I_main: sub rsp, 8 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev add rsp, 8 jmp __cxa_atexit
ich sage ja auch nicht, dass oo generell schlecht ist, sondern nur, dass mir die ständigen aussetzer bei prision architect total auf den keks gehen, weil da ständig solche kopierereien stattfinden werden. also ich gehe mal davon aus, dass die leute im studium aufgepasst haben und sich an gängige programmierstandards wie datenkapselung halten.
-
@Wade1234 sagte in String einlesen lassen (Klassen):
ich sage ja auch nicht, dass oo generell schlecht ist, sondern nur, dass mir die ständigen aussetzer bei prision architect total auf den keks gehen, weil da ständig solche kopierereien stattfinden werden.
Ich würde mir zu so etwas keine Aussage zutrauen, ohne den Quellcode gesehen und mit einem Profiler genau analysiert zu haben.
Allerdings halte ich es für sehr unwahrscheinlich, dass die hier diskutierte Getter/Setter-Design selbst in der ineffizientesten Variante zu "Aussetzern" führt. Die Unterschiede liegen hier im Mikrosekunden-Bereich, da würde ich eher "allgemeine Langsamkeit" erwarten, falls sich das überhaupt spürbar oder sogar messbar auswirkt.
Wenn ich ein Programm mit "Aussetzern" habe, dann würde ich mir eher als erstes die Algorithmen ansehen, statt nach nicht umgesetzten Mikrooptimierungen zu suchen.