Czy dobrym pomysłem jest „#define me (* this)”?

19

To makro można zdefiniować w nagłówku globalnym lub lepiej jako parametr wiersza polecenia kompilatora:

#define me (*this)

I przykład użycia:

some_header.h:

inline void Update()
{
    /* ... */
}

main.cpp:

#include "some_header.h"

class A {
public:
    void SetX(int x)
    {
        me.x = x;   
        me.Update(); 
    }

    void SomeOtherFunction()
    {
        ::Update();
    }

    /*
        100 or more lines
        ... 
    */

    void Update()
    {
        // ... 
    }

    int x;  
};

Tak więc w metodzie klasowej, kiedy uzyskuję dostęp do członka klasy, zawsze używam me, a podczas uzyskiwania dostępu do identyfikatora globalnego zawsze używam ::. To daje czytelnikowi, który nie jest zaznajomiony z kodem (prawdopodobnie po kilku miesiącach) zlokalizowane informacje o tym, do czego można uzyskać dostęp bez konieczności szukania gdzie indziej. Chcę zdefiniować, meponieważ uważam, że używanie this->wszędzie jest zbyt głośne i brzydkie. Ale czy można #define me (*this)to uznać za dobrą praktykę w C ++? Czy są jakieś praktyczne problemy z memakrem? A jeśli jako programista C ++ będziesz czytał jakiś kod za pomocą memakra, chciałbyś go, czy nie?

Edycja: Ponieważ wiele osób twierdzi, że nie przeciwstawia się temu me, ale ogólnie przeciwstawia się temu wyraźnie. Myślę, że może nie być jasne, jakie są korzyści z „wyraźnego wyrażenia tego wszędzie”.

Jakie są korzyści z „wyrażania tego wszędzie”?

  • Jako czytelnik kodu masz pewność, co jest dostępne i możesz skoncentrować się na innych rzeczach niż zweryfikować - w jakimś odległym kodzie - czy naprawdę można uzyskać dostęp do tego, co według ciebie jest dostępne.
  • Możesz użyć funkcji wyszukiwania bardziej szczegółowo. Wyszukiwanie „ this->x” może dać więcej pożądanych wyników niż tylko wyszukiwanie „ x
  • Kiedy usuwasz lub zmieniasz nazwę jakiegoś członka, kompilator niezawodnie powiadamia cię w miejscach, w których ten członek jest używany. (Niektóre funkcje globalne mogą mieć tę samą nazwę i istnieć szansa, że ​​możesz wprowadzić błąd, jeśli nie używasz tego jawnie).
  • Kiedy refaktoryzujesz kod i czynisz funkcję inną niż element członkowską (aby uzyskać lepszą enkapsulację) jawnie, pokazuje to miejsce, które musisz edytować i możesz łatwo zastąpić to wskaźnikiem do instancji klasy podanej jako parametr funkcji niebędącej członkiem
  • Zasadniczo, gdy zmieniasz kod, istnieje więcej możliwości wystąpienia błędów, gdy nie używasz tego jawnie, niż gdy używasz tego jawnie wszędzie.
  • Wyraźnie to jest mniej głośne niż wyraźne „m_”, gdy uzyskujesz dostęp do członka z zewnątrz ( object.membervs object.m_member) (dzięki @Kaz zauważył ten punkt)
  • Wyjaśnij, że rozwiązuje to problem uniwersalnie dla wszystkich członków - atrybutów i metod, podczas gdy „m_” lub inny prefiks jest praktycznie użyteczny tylko dla atrybutów.

Chciałbym dopracować i rozszerzyć tę listę, powiedzieć mi, czy wiesz o innych zaletach, i użyć przypadków, aby to wszędzie wyrazić .

użytkownik3123061
źródło
3
Powinieneś unikać argumentów funkcji i członków o tej samej nazwie, a także unikaj wyraźnego „tego”.
pjc50
4
Przypomina mi kod VB mojego szefa. Me Me Me wszędzie. Brzmi samolubnie. : p W każdym razie odpowiedzi jak dotąd mówią wszystko.
MetalMikester
19
To wspaniały pomysł! A dla tych, którzy pochodzą z języka Python, dlaczego nie #define self (*this)? Możesz nawet mieszać oba makra i mieć niektóre pliki imitujące VB i inne Python. :)
logc
11
Dlaczego po prostu nie pójść na całość i zrobić: #include "vb.h", #Include pascal.halbo #include FOTRAN.hi mieć kolejną osobę dotknąć kod przedkłada go TDWTF .
Dan Neely
4
Och, proszę, po prostu nie. Możesz zaoszczędzić sobie kłopotów, po prostu deklarując atrybuty jako me_x.
Gort the Robot

Odpowiedzi:

90

Nie, nie jest.

Zwróć uwagę na programistę, który będzie utrzymywał Twój kod za kilka lat, długo po tym, jak wyszedłeś na bardziej zielone pastwiska i postępuj zgodnie z powszechnymi konwencjami języka, którego używasz. W C ++ prawie nigdy nie musisz pisać this, ponieważ klasa jest zawarta w kolejności rozpoznawania symboli, a gdy symbol zostanie znaleziony w zakresie klasy, this->jest to implikowane. Więc nie pisz tego tak, jak wszyscy.

Jeśli często się mylisz, które symbole pochodzą z zakresu klasy, zwykle stosuje się wspólny wzorzec nazewnictwa dla członków (pola i czasami metody prywatne; nie widziałem, aby był używany do metod publicznych). Najczęstsze z nich obejmują sufiks _lub prefiks za pomocą m_lub m.

Jan Hudec
źródło
12
„W C ++ prawie nigdy nie musisz tego pisać” , jest to całkowicie niepowiązana konwencja do pytania. Niektóre osoby (jak ja) wolą pisać, thisaby wyraźnie zaznaczyć, że zmienna nie jest lokalna dla metody. Pytanie dotyczy tego, czy makro mepowinno istnieć, a nie, czy thisnależy tego użyć.
Mehrdad
8
@Mehrdad: Ponieważ PO wyraźnie mówi, że używa me.zamiast this->. Ponieważ nie ma takiej potrzeby, this->nie ma takiej potrzeby, me.a zatem nie ma potrzeby makra.
Martin York
11
@LokiAstari: Nic w pytaniu nawet zdalnie nie wskazuje OP, zastanawiając się, czy this->„jest konieczne”. W rzeczywistości PO twierdzi, że używa, this->mimo że nie jest to konieczne. Pytanie dotyczy tego, czy me.należy użyć zamiast tego this-> , na co ta odpowiedź w rzeczywistości nie odpowiada.
Mehrdad
4
@ Mehrdad: Ale samo wstawienie tak lub nie daje bardzo nudną odpowiedź. Tak więc wyjaśnienia są zwykle bardzo przydatne w wyjaśnianiu, w jaki sposób można uzyskać odpowiedź (jest to zawarte w How to answer ). Konkluzji nie należy wykorzystywać, ponieważ OP ma niewłaściwe podejście do korzystania z niej, thisdlatego też dyskusja na ten temat jest wymagana, aby osiągnąć w pełni zrównoważony wniosek.
Martin York,
1
+1 za wyjaśnienie, dlaczego zamiast mówić „Nie, to zła praktyka”.
user253751
42

Więc chcesz stworzyć nowy język. Następnie zrób to i nie kalekuj C ++.

Istnieje kilka powodów, aby tego nie robić:

  1. Każdy normalny standard kodowania sugeruje unikanie makr ( oto dlaczego )
  2. Przy takich makrach trudniej jest utrzymywać kod. Każdy programujący w C ++ wie cothis jest, a dodając takie makro, w rzeczywistości dodajesz nowe słowo kluczowe. Co jeśli wszyscy przedstawią coś, co lubią? Jak wyglądałby kod?
  3. Nie powinieneś używać (*this).ani this->wcale, z wyjątkiem niektórych szczególnych przypadków (zobacz tę odpowiedź i wyszukaj „to->”)

Twój kod nie różni się od #define R return , który widziałem w rzeczywistym kodzie. Powód? Mniej pisania!


Przechodzę nieco poza tematem, ale tutaj zamierzam rozwinąć punkt 3 (nie używaj (*this). ani this->na zajęciach).

Po pierwsze, (*this). czy this->są wykorzystywane do zmiennych składowych dostępu lub funkcji obiektu. Korzystanie z niego jest bez znaczenia i oznacza więcej pisania. Ponadto czytanie takiego kodu jest trudniejsze, ponieważ jest więcej tekstu. To oznacza trudniejszą konserwację.

Więc jakie są przypadki, w których musisz użyć this-> ?

(a) Niefortunny wybór nazwy argumentu.

W tym przykładzie this->jest wymagany, ponieważ argument ma taką samą nazwę jak zmienna elementu:

struct A {
  int v;
  void foo( int v ) {
    this->v =v;
  }
};

(b) W przypadku szablonów i dziedziczenia (patrz to )

W tym przykładzie kompilacja nie powiedzie się, ponieważ kompilator nie wie, do której zmiennej vnależy uzyskać dostęp.

template< typename T >
struct A {
  A(const T& vValue):v(vValue){}

  T v;
};

template< typename T >
struct B : A<T>
{
    B(const T& vValue):A<T>(vValue){}

    void foo( const T & newV ) {
      v = newV;
    }
};
BЈовић
źródło
1
„Więc chcesz stworzyć nowy język. Następnie zrób to i nie kalekuj c ++.” - Nie zgadzam się. Główną zaletą C i C ++ jest ich elastyczność. OP nie osłabia C ++, po prostu wykorzystuje swoją elastyczność w określony sposób, aby ułatwić mu kodowanie.
user253751
2
@immibis Right. To, co jest dla niego łatwiejsze, jest trudniejsze dla wszystkich innych. Kiedy pracujesz w zespole, umieszczenie takich bzdur w kodzie nie sprawi, że będziesz bardzo przychylny.
BЈовић
2
Twoja odpowiedź nie różni się od „definicje są złe”.
Pan Lister
7
@MrLister Moja odpowiedź brzmi: „głupie definicje są złe”. Makro w pytaniu jest głupie w użyciu i, jak pokazałem, wcale nie jest potrzebne. Kod jest dobry, a nawet lepszy bez *this.ithis->
15овић
1
@ Mehrdad Czy ludzie nadal dodają prefiksy i sufiksy do zmiennych członków? Myślałem, że to „naprawiono” w przypadku książek takich jak Clean Code . this->jest równie użyteczny jak autow wersji wcześniejszej niż c ++ 11. Innymi słowy, po prostu zwiększa szum kodu.
BЈовић
26

Radzę tego nie robić. To daje czytelnikowi, który nie zna Twojego makra, duży „ WTF ”, gdy tylko to zobaczy. Kod nie staje się bardziej czytelny przy wymyślaniu „nowych konwencji” w stosunku do ogólnie przyjętych bez rzeczywistej potrzeby.

używanie tego-> wszędzie jest zbyt głośne i brzydkie

Może ci się to wydawać, być może dlatego, że dużo programowałeś w językach przy użyciu słowa kluczowego me(chyba Visual Basic?). Ale tak naprawdę to tylko kwestia przyzwyczajenia się do tego - this->jest dość krótka i myślę, że większość doświadczonych programistów C ++ nie zgodzi się z twoją opinią. A w powyższym przypadku ani użycie, this->ani użycie nie mejest właściwe - najmniejszy bałagan, pozostawiając te słowa kluczowe poza dostępem do członków danych wewnątrz funkcji członka.

Jeśli chcesz, aby twoje prywatne zmienne składowe były odróżniane od lokalnych, dodaj coś link m_jako przedrostek lub podkreślnik jako przyrostek (ale jak widać tutaj , nawet ta konwencja jest „zbyt głośna” dla wielu osób).

Doktor Brown
źródło
12
_powinien być przyrostek ; jako przedrostek jest zarezerwowany dla wewnętrznych symboli standardowych bibliotek i rozszerzeń dostawcy.
Jan Hudec
4
@JanHudec Tylko jeśli zaczyna się od __(dwa podkreślenia) lub podkreślenia, po których następuje duża litera. Pojedynczy znak podkreślenia, po którym następuje mała litera, jest w porządku, o ile nie znajduje się w globalnej przestrzeni nazw.
Eric Finn
1
@EricFinn: Połączyłem dwie reguły w jedną. Dwa znaki podkreślenia lub podkreślenia, po których następuje wielka litera, oznaczają symbole wewnętrzne, a pojedynczy znak podkreślenia, po którym następuje mała litera, to rozszerzenia specyficzne dla systemu. Aplikacje też nie powinny używać.
Jan Hudec
@JanHudec Nie był świadomy reguły dotyczącej rozszerzeń systemowych. Zostawię jednak mój poprzedni komentarz, aby podać kontekst dla twojego komentarza.
Eric Finn
2
@JanHudec: Małe litery dotyczą rozszerzeń specyficznych dla systemu? Czy możesz zamieścić odniesienie do tej części standardu C ++, która to wskazuje?
Mehrdad
18

Proszę nie rób tego! Próbuję poradzić sobie z dużą bazą kodu, gdzie makra są wszędzie, aby zaoszczędzić pisania. Złą rzeczą w przedefiniowaniu tego dla mnie jest to, że preprocesor zastąpi go wszędzie, nawet jeśli nie jest to objęte zakresem / nie ma zastosowania, na przykład samodzielna funkcja, twój kolega z pracy może mieć lokalną zmienną o nazwie mnie gdzie indziej .. (s) nie będzie szczęśliwy debugowania ... W końcu masz makra, których nie możesz używać we wszystkich zakresach.

truschival
źródło
6

NIE!

Wyobraź sobie zamieszanie, które wystąpi, jeśli ktoś # dołącza ten nagłówek, nie znając twojej sztuczki, a gdzie indziej w swoim pliku mają zmienną lub funkcję o nazwie „ja”. Byliby okropnie zdezorientowani tym, co wydrukowałoby nieprzenikniony komunikat o błędzie.

Larry Gritz
źródło
W praktyce całkiem prawdopodobne jest, że najechają myszką „ja” za pomocą swojego IDE i zobaczą definicję.
user253751
2
@immibis: W praktyce jednak mogą też powiedzieć coś w stylu „kto napisał to badziewie?”. : P
cHao
@immibis: większość najlepszych programistów, z którymi pracuję, nie używa myszy - jest zbyt wolna.
JBRWilkinson
Rzeczywiście: wrzucenie tego do nagłówka jest faktycznym problemem. Jeśli użyjesz go w pliku CPP, nie widzę problemu. Gdybyśmy mieli standard pragma push, doradziłbym, jak to zrobić w nagłówku, ale nie robimy tego. Powtarzaj za mną. # definicje są globalne.
Joshua