Różnice między łańcuchem C ++ == a Compare ()?

363

Właśnie przeczytałem kilka zaleceń dotyczących używania

std::string s = get_string();
std::string t = another_string();

if( !s.compare(t) ) 
{

zamiast

if( s == t )
{

Prawie zawsze używam ostatniego, ponieważ jestem do tego przyzwyczajony i wydaje mi się to naturalne, bardziej czytelne. Nawet nie wiedziałem, że istnieje osobna funkcja porównania. Mówiąc ściślej, pomyślałem, że == wywoła funkcję Compare ().

Jakie są różnice? W jakich kontekstach należy preferować jeden sposób na drugi?

Rozważam tylko przypadki, w których muszę wiedzieć, czy łańcuch ma taką samą wartość jak inny łańcuch.

Klaim
źródło
5
Pierwszy zwróci prawdę, a drugi zwróci fałsz i odwrotnie.
Viktor Sehr
56
Pierwszy jest ledwo czytelny, a drugi jest łatwy do odczytania i zrozumienia.
Matthieu M.
3
Używam funkcji „porównaj” w ten sposób: if(x.compare(y) == 0)<- znak równości, jest równy. Używanie IMO !służy tylko do tego, aby kod był nieczytelny.
R. Martinho Fernandes,
1
Należy zauważyć, że == nie będzie dla ciebie działać w każdym przypadku. string przeciąża operatora, aby wykonać porównanie, więc == to to samo, co wywołanie porównania. Alternatywnie, jeśli spróbujesz tego na obiektach, które nie przeciążają operatora ==, będziesz porównywał ich adres w pamięci, a nie ich wewnętrzne komponenty. Porównywanie połączeń jest bardziej „bezpieczne”. Jednak w przypadku użycia std :: string wszystko jest w porządku.
DCurro
Jedna różnica: comparezwrot, -1jeśli sjest niższy niż ti +1jeśli sjest większy niż tpodczas ==powrotu true/false. Niezerowe liczby całkowite są truei 0false.
GyuHyeon Choi

Odpowiedzi:

450

O tym mówi standard operator==

21.4.8.2 operator ==

template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT,traits,Allocator>& lhs,
                const basic_string<charT,traits,Allocator>& rhs) noexcept;

Zwraca: lhs.compare (rhs) == 0.

Wygląda na to, że nie ma dużej różnicy!

Bo Persson
źródło
5
Uwaga dla czytelników: Proszę przeczytać odpowiedź Frédérica Hamidi, aby uzyskać szczegółowe informacje na ten temat, ponieważ istnieją istotne różnice. Chociaż cieszę się, że Bo Persson pokazuje, że dwa testy na pewno zwrócą tę samą wartość. !s.compare(t)i s == tzwróci tę samą wartość, ale funkcja porównywania dostarcza więcej informacji s == ti s == tjest bardziej czytelna, gdy nie obchodzi cię, jak różnią się ciągi, ale tylko jeśli się różnią.
cdgraham
143

std :: string :: Compare () zwraca an int:

  • równa zero, jeśli si tsą równe,
  • mniej niż zero, jeśli sjest mniejsze niż t,
  • większy od zera, jeśli sjest większy niż t.

Jeśli chcesz, aby pierwszy fragment kodu był równoważny z drugim, powinien on w rzeczywistości brzmieć:

if (!s.compare(t)) {
    // 's' and 't' are equal.
}

Operator równości sprawdza tylko równość (stąd jego nazwa) i zwraca a bool.

Rozwinięcie przypadków użycia compare()może być przydatne, jeśli interesuje Cię, w jaki sposób te dwa ciągi odnoszą się do siebie (mniej lub więcej), kiedy są różne. PlasmaHH słusznie wspomina o drzewach, może to być również, powiedzmy, algorytm wstawiania ciągów, który ma na celu utrzymanie sortowania kontenera, dychotomiczny algorytm wyszukiwania dla wspomnianego wyżej kontenera i tak dalej.

EDYCJA: Jak zauważa Steve Jessop w komentarzach, compare()jest najbardziej przydatny do algorytmów szybkiego sortowania i wyszukiwania binarnego. Naturalne sortowanie i wyszukiwania dychotomiczne mogą być realizowane tylko przy pomocy std :: less .

Frédéric Hamidi
źródło
zwróć uwagę, że takie zachowanie jest często przydatne w kontaktach z drzewami lub podobnymi do drzew stworzeniami.
PlasmaHH
Rzeczywiście, wskazywałam tylko na różnice między metodą a operatorem równości :)
Frédéric Hamidi
„W jakich kontekstach należy preferować jeden ze sposobów w inny sposób?” po prostu sprawia, że ​​myślę, że OP nie może wymyślić możliwych przypadków użycia do porównania ().
PlasmaHH
2
„jeśli jesteś zainteresowany tym, jak te dwa ciągi odnoszą się do siebie nawzajem” - chociaż idiomatyczne C ++ do tego celu polega na użyciu ścisłej słabej kolejności (jak std::lessw tym przypadku, która jest również całkowitą kolejnością) zamiast trójstronnego komparatora . compare()jest dla operacji wzorowanych na std::qsorti std::bsearch, w przeciwieństwie do operacji wzorowanych na std:sorti std::lower_bound.
Steve Jessop
30

comparema przeciążenia do porównywania podciągów. Jeśli porównujesz całe ciągi znaków, powinieneś po prostu użyć ==operatora (i to, czy wywołuje, compareczy nie, nie ma większego znaczenia).

Cat Plus Plus
źródło
30

Wewnętrznie string::operator==()używa string::compare(). Proszę odnieść się do: CPlusPlus -string::operator==()

Napisałem małą aplikację do porównania wydajności i najwyraźniej jeśli skompilujesz i uruchomisz swój kod w środowisku debugowania, string::compare()jest to nieco szybsze niż string::operator==(). Jednak jeśli skompilujesz i uruchomisz kod w środowisku Release, oba są prawie takie same.

Do Twojej wiadomości przeprowadziłem 1 000 000 iteracji, aby dojść do takiego wniosku.

Aby udowodnić, dlaczego w środowisku debugowania łańcuch :: porównywanie jest szybszy, poszedłem do zestawu i oto kod:

DEBUGOWANY BUDYNEK

string :: operator == ()

        if (str1 == str2)
00D42A34  lea         eax,[str2]  
00D42A37  push        eax  
00D42A38  lea         ecx,[str1]  
00D42A3B  push        ecx  
00D42A3C  call        std::operator==<char,std::char_traits<char>,std::allocator<char> > (0D23EECh)  
00D42A41  add         esp,8  
00D42A44  movzx       edx,al  
00D42A47  test        edx,edx  
00D42A49  je          Algorithm::PerformanceTest::stringComparison_usingEqualOperator1+0C4h (0D42A54h)  

string :: porównaj ()

            if (str1.compare(str2) == 0)
00D424D4  lea         eax,[str2]  
00D424D7  push        eax  
00D424D8  lea         ecx,[str1]  
00D424DB  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::compare (0D23582h)  
00D424E0  test        eax,eax  
00D424E2  jne         Algorithm::PerformanceTest::stringComparison_usingCompare1+0BDh (0D424EDh)

Widać, że w string :: operator == () musi wykonywać dodatkowe operacje (dodać esp, 8 i movzx edx, al)

ZWOLNIENIE BUDYNKU

string :: operator == ()

        if (str1 == str2)
008533F0  cmp         dword ptr [ebp-14h],10h  
008533F4  lea         eax,[str2]  
008533F7  push        dword ptr [ebp-18h]  
008533FA  cmovae      eax,dword ptr [str2]  
008533FE  push        eax  
008533FF  push        dword ptr [ebp-30h]  
00853402  push        ecx  
00853403  lea         ecx,[str1]  
00853406  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::compare (0853B80h)  

string :: porównaj ()

            if (str1.compare(str2) == 0)
    00853830  cmp         dword ptr [ebp-14h],10h  
    00853834  lea         eax,[str2]  
    00853837  push        dword ptr [ebp-18h]  
    0085383A  cmovae      eax,dword ptr [str2]  
    0085383E  push        eax  
    0085383F  push        dword ptr [ebp-30h]  
    00853842  push        ecx  
00853843  lea         ecx,[str1]  
00853846  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::compare (0853B80h)

Oba kody zestawu są bardzo podobne, ponieważ kompilator wykonuje optymalizację.

Wreszcie, moim zdaniem, wzrost wydajności jest znikomy, dlatego naprawdę pozostawiłbym programistom decyzję, który z nich jest preferowany, ponieważ oba osiągają ten sam wynik (szczególnie, gdy jest to kompilacja wydania).

Tony Mulia
źródło
10
„bardzo podobne” ... nie widzę różnicy, prawda?
xtofl
ja też ... one są tym samym. nie ma różnicy
Wagner Patriota
1
@xtofl z przykładu Tony'ego wygenerowane kody są identyczne w kompilacji wydania, różnią się w kompilacjach debugowania.
JulianHarty
6

compare()jest równoważne strcmp (). ==to proste sprawdzanie równości. compare()dlatego zwraca an int, ==boolean.

ckruse
źródło
5

compare()zwróci false(cóż, 0) jeśli łańcuchy są równe.

Więc nie lekceważ jednego za drugiego.

Użyj tego, co czyni kod bardziej czytelnym.

Luchian Grigore
źródło
3

Jeśli chcesz tylko sprawdzić równość łańcucha, użyj operatora ==. Ustalenie, czy dwa ciągi są równe, jest prostsze niż znalezienie uporządkowania (co daje porównanie ()), więc w twoim przypadku może być lepiej pod względem wydajności użycie operatora równości.

Dłuższa odpowiedź: interfejs API zapewnia metodę sprawdzania równości ciągów oraz metodę sprawdzania kolejności ciągów. Chcesz równości łańcuchów, więc użyj operatora równości (aby dopasować swoje oczekiwania i implementatory bibliotek). Jeśli wydajność jest ważna, możesz przetestować obie metody i znaleźć najszybszą.

RobH
źródło
2

Załóżmy, że rozważmy dwa ciągi si t.
Podaj im pewne wartości.
Kiedy porównasz je za pomocą (s == t) , zwraca wartość logiczną (prawda lub fałsz, 1 lub 0).
Ale gdy porównujesz za pomocą s.compare (t) , wyrażenie zwraca wartość
(i) 0 - jeśli si it są równe
(ii) <0 - albo jeśli wartość pierwszego niedopasowanego znaku w s jest mniejsza niż wartość t lub długość s jest mniejsza niż długość t.
(iii) > 0 - jeśli wartość pierwszego niedopasowanego znaku wt jest mniejsza niż s lub długość t jest mniejsza niż s.

narutoUzumaki21
źródło
1

Jedną rzeczą, która nie jest tutaj omawiana, jest to, że zależy to od porównania łańcucha do łańcucha c, łańcucha do łańcucha lub łańcucha do łańcucha.

Główną różnicą jest to, że do porównania dwóch łańcuchów równość wielkości jest sprawdzana przed wykonaniem porównania, co sprawia, że ​​operator == jest szybszy niż porównanie.

oto porównanie, jakie widzę na g ++ Debian 7

// operator ==
  /**
   *  @brief  Test equivalence of two strings.
   *  @param __lhs  First string.
   *  @param __rhs  Second string.
   *  @return  True if @a __lhs.compare(@a __rhs) == 0.  False otherwise.
   */
  template<typename _CharT, typename _Traits, typename _Alloc>
    inline bool
    operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
           const basic_string<_CharT, _Traits, _Alloc>& __rhs)
    { return __lhs.compare(__rhs) == 0; }

  template<typename _CharT>
    inline
    typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value, bool>::__type
    operator==(const basic_string<_CharT>& __lhs,
           const basic_string<_CharT>& __rhs)
    { return (__lhs.size() == __rhs.size()
          && !std::char_traits<_CharT>::compare(__lhs.data(), __rhs.data(),
                            __lhs.size())); }

  /**
   *  @brief  Test equivalence of C string and string.
   *  @param __lhs  C string.
   *  @param __rhs  String.
   *  @return  True if @a __rhs.compare(@a __lhs) == 0.  False otherwise.
   */
  template<typename _CharT, typename _Traits, typename _Alloc>
    inline bool
    operator==(const _CharT* __lhs,
           const basic_string<_CharT, _Traits, _Alloc>& __rhs)
    { return __rhs.compare(__lhs) == 0; }

  /**
   *  @brief  Test equivalence of string and C string.
   *  @param __lhs  String.
   *  @param __rhs  C string.
   *  @return  True if @a __lhs.compare(@a __rhs) == 0.  False otherwise.
   */
  template<typename _CharT, typename _Traits, typename _Alloc>
    inline bool
    operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
           const _CharT* __rhs)
    { return __lhs.compare(__rhs) == 0; }
Dragos
źródło
kod jest sformatowany i wyświetlony w formacie edytora. Wyświetlacz źle to zrozumiał. otwórz plik Basic_string.h i poszukaj operatora == w swoim systemie operacyjnym. Kod nie jest mój jest standardem, fakt, że w tym wątku brakuje kontroli rozmiaru. Widzę też, że wiele osób zgadza się z niepoprawnymi informacjami, które są sprzeczne z użytecznością Przepełnienia stosu.
Dragos,