Operator yüklənməsi üçün əsas qaydalar və deyimlər hansılardır?

Qeyd Cavablar müəyyən bir qaydada verilmişdi, amma çox istifadəçi cavabı verdikləri vaxt deyil, səslərə görə sıraladıqları üçün, cavabların indeksləri ən mənalı şəkildə düzəldilmişdir:

<sub> (Qeyd: Bu , C ++ yığın taşması haqqında Sık Sorulan Sorulara girdiğiniz anlamına gelir. Bu forma bir FAQ verme fikrini tənqid etmək istəsəniz, o zaman başlayan meta yazıları bunun üçün bir yer olacaktır. Bu suallara cavablar C ++ söhbətində izlənilir, burada FAQ fikirləri ilk növbədə başlamışdır, buna görə cavabınız bu fikri yaradanlar tərəfindən oxunacaqdır.)

1936
12 дек. set sbi 12 dec. 2010-12-12 15:44 '10 at 15:44 2010-12-12 15:44
@ 7 cavab

Yeniləmə üçün baş operatorlar

Aşırı yük operatorlarının çoxu qazan otağının kodu. Bu, təəccüblü deyil, çünki operatorlar sadəcə sintaktik şəkərdir, əsl işi sadə funksiyaları yerinə yetirə bilər (və tez-tez yönəldilə bilər). Lakin qazan kodunu almaq vacibdir. Başarısız olsanız, ya operator kodunuz tərtib edilməyəcək, yaxud istifadəçi kodları tərtib edilməyəcək və ya istifadəçi kodu şaşırtıcı görünməyəcəkdir.

Atama operatoru

Randevu haqqında çox şey söyləmək olar. Lakin, onların əksəriyyəti məşhur "Mop və Swap Suallar" da deyilmişdir, buna görə buradan ən çox keçmək lazımdır, yalnız link üçün ideal təyinat operatoru:

 X X::operator=(X rhs) { swap(rhs); return *this; } 

Bitşift operatorlar (Stream I / O üçün istifadə olunur)

Cihazdan istifadə edən bit-manipulyasiya funksiyaları üçün hardware interfeysində hələ də istifadə olunan bit-shift operatorları <<>> olduqca çox proqramlarda yüklənmiş giriş və çıxış axın operatorları kimi daha geniş yayılmışdır. Bits manipulyasiya operatorları kimi yükləmə təlimatları üçün ikili aritmetik əməliyyatlar üzrə aşağıdakı bölməyə baxın. Nesneniz iostreams ilə istifadə edildikdə, öz xüsusi formatınızı həyata keçirmək və məntiqi təhlil etmək üçün davam edin.

Ən çox reloadable operatorlar arasında axın operatorları ikili infix operatorlarıdır, sintaksisində üzv olmaları və ya qeyri-üzv olması barədə məhdudiyyətlər göstərilmir. Çünki sol dalilini dəyişdirirlər (onlar mövzuların vəziyyətini dəyişdirirlər), baş qayda qaydalarına uyğun olaraq, sol əlli operandların üzvləri kimi həyata keçirilməlidirlər. Lakin, sol operandlar standart kitabxana axınlarıdır və standart kütüphaneyə tərəfindən müəyyən edilən axın çıxışları və giriş operatorlarının əksəriyyəti, həqiqətən, öz növlərinə görə çıxış və giriş əməliyyatları tətbiq etdiyiniz zaman, mövzu siniflərinin üzvləri kimi müəyyən edilirsə də, standart tipli kitabxana axınlarını dəyişə bilməzsiniz . Buna görə, bu operatorları qeyri-üzv funksiyalar kimi öz növlərinizə tətbiq etməlisiniz. Bunların ikitərəfli formaları aşağıdakılardır:

 std::ostream operator<<(std::ostream os, const T obj) { // write obj to stream return os; } std::istream operator>>(std::istream is, T obj) { // read obj from stream if(  ) is.setstate(std::ios::failbit); return is; } 

operator>> əl tətbiq edərkən, mövzuların vəziyyətini müəyyənləşdirmək yalnız oxunuşun özü edildikdə vacibdir, lakin nəticə gözlənilənə uyğun deyil.

Function Call Operator

Funktsiyalar kimi tanınan funksional obyektləri yaratmaq üçün istifadə olunan funksiya çağırışı operatoru üzv funksiyası kimi müəyyən edilməlidir, buna görə də həmişə üzv funksiyalarının this arqumentini ört-basdır. Bundan əlavə, sıfır daxil olmaqla, hər hansı bir sıra əlavə arqumentləri qəbul etmək üçün çox yüklənə bilər.

Sintaksisin nümunəsi:

 class foo { public: // Overloaded call operator int operator()(const std::string y) { // ... } }; 

İstifadə edin:

 foo f; int a = f("hello"); 

Standart C ++ kitabxanasında obyekt obyektləri həmişə kopyalanır. Buna görə, öz funksiyanızın obyektləri kopyalanmaq üçün ucuz olmalıdır. Funksiya obyektinin kopyalanmaq üçün bahalı olan məlumatları istifadə etməsi üçün tamamilə zəruri olduqda, bu məlumatı başqa yerlərdə saxlamaq və funksiya obyektinə müraciət etmək daha yaxşıdır.

Müqayisəli operatorlar

Binary infix müqayisəsi operatorları, başqalardakı qaydalara uyğun olaraq, qeyri-üzv funksiyaları kimi tətbiq edilməlidir1. Unitar prefiks inkar ! (eyni qaydalara uyğun olaraq) üzv funksiyası kimi həyata keçirilməlidir. (lakin, bir qayda olaraq, onu yükləmək tövsiyə edilmir.)

Standart kitabxana alqoritmləri (məsələn, std::sort() ) və növləri (məsələn, std::map ) həmişə operator< gözləyir. Bununla belə, sizin tipli istifadəçilər bütün digər operatorların da mövcud olmasını gözləyirlər, belə ki, operator< müəyyənləşdirsəniz, operator yüklənməsinin üçüncü əsas qayda ilə əməl etdiyinizə əmin olun və bütün digər mantıksal müqayisəli operatorları müəyyənləşdirin. Bunları həyata keçirmək üçün qanunauyğun yollar aşağıdakılardır:

 inline bool operator==(const X lhs, const X rhs){  } inline bool operator!=(const X lhs, const X rhs){return !operator==(lhs,rhs);} inline bool operator< (const X lhs, const X rhs){  } inline bool operator> (const X lhs, const X rhs){return operator< (rhs,lhs);} inline bool operator<=(const X lhs, const X rhs){return !operator> (lhs,rhs);} inline bool operator>=(const X lhs, const X rhs){return !operator< (lhs,rhs);} 

Qeyd etmək vacibdir ki, bu operatorlardan yalnız ikisi həqiqətən bir şey edirlər, bəziləri sadəcə əsl işi yerinə yetirmək üçün öz arqumentlərini bu ikisinin heç birinə yönəldə bilməzlər.

Qalan ikili boolean operatorları ( || , > ) yüklənmə sintaksisi müqayisə operatorlarının qaydalarına əməl edir. Bununla belə, bu 2 üçün məqbul bir presedent tapa bilərsiniz çox çətin deyil.

1 Başqalar bütün qaydalarına baxmayaraq bəzən bu birinin pozulmasına səbəb ola bilər. Əgər belədirsə, üzv funksiyaları üçün *this olduğu üçün ikili müqayisə operatorlarının sol operandının da const olması vacibdir. Beləliklə, üzv funksiyası kimi tətbiq edilən müqayisəli operator bu imza olmalıdır:

 bool operator<(const X rhs) const {  } 

(Sonunda const qeyd edin.)

2 Qeyd edək ki, quraşdırılmış versiya || > etiket semantikasını istifadə edir. İstifadəçi müəyyən etdikləri halda (metod çağırışları üçün sintaktik şəkər olduğu üçün) etiket semantikasını istifadə etməyin. İstifadəçi bu operatorların etiket semantikası olmasını gözləyir və kodları ondan asılı ola bilər. Buna görə də, onları heç vaxt tanımamaq tövsiyə olunur.

Aritmetik operatorlar

Unary arifmetik operatorları

Unary artım və azalma operatorları prefiks və postfiks tatlarında da mövcuddur. Bir-birindən bəhs etmək üçün, postfiks variantları dummy int'ün isteğe bağlı arqumentini alır. Artım və ya azalma yükləndiyiniz halda, hər zaman həm prefiks, həm də postfix versiyalarını istifadə edəcəyinizə əmin olun. Artımın kanonik tətbiqi burada azalma eyni qaydalara uyğundur:

 class X { X operator++() { // do actual increment return *this; } X operator++(int) { X tmp(*this); operator++(); return tmp; } }; 

Postfix variantının prefiks baxımından yerinə yetirildiyini unutmayın. Bundan əlavə, postfix əlavə surəti yerinə yetirir. 2

Unary minus və artı yüklənməsi çox yaygın deyil və yəqin ki, ən yaxşı yoldan çəkinir. Lazım olarsa, ehtimal ki, üzv funksiyaları kimi şişirdilməlidir.

2 Bundan əlavə, postfix variantının daha çox işi olduğunu və bu səbəbdən prefiks versiyasından istifadə etmək daha az effektiv olduğunu unutmayın. Bu yaxşı bir səbəbdir, bir qayda olaraq postfix'i artıraraq prefiks artırmağa üstünlük verir. Derleyiciler, genellikle daxili türler üçün ek artan işleri optimize edə bilərlər, baxmayaraq ki, xüsusi növlər üçün eyni şeyi edə bilməzlər (siyahı yineleyici kimi günahsız bir şey ola bilər). i++ istifadə etmək üçün istifadə edildikdən sonra ++i əvəzinə bir daxili növü olmayan (yəni tipi dəyişdirərkən kodu dəyişdirməlisiniz) əvəzinə yadda saxlamaq çox çətindir, belə ki, postfiks istisna olmaqla, hər zaman prefiks artımından istifadə etmək üçün ən yaxşı seçimdir tələb olunur.

İkili aritmetik operatorlar

İkili aritmetik operatorlar üçün, əsas qayda operatorunun üçüncü yüklənməsini yerinə yetirməyi unutmayın: əgər təmin etsəniz, += təmin etsəniz, -= qaçırmayın -= və s. Andrei Koeniqin kompleks təyinat agentlərinin qeyri-kompozisiya nüsxələrinin əsası kimi istifadə edilə biləcəyini ilk dəfə qeyd etdiyini söyləyir. Yəni operator += , -= və s. Baxımından yerinə yetirilir.

Baş parmağımızın qaydalarına uyğun olaraq + və onun peykləri qeyri-üzv olmalı, onların sol arqumentini dəyişdirən birləşmələr müqayisə edilərkən ( += və s.) Üzv olmalıdır. Burada +=+ üçün nümunə kodudur, digər ikili aritmetik operatorlar da eyni şəkildə həyata keçirilməlidir:

 class X { X operator+=(const X rhs) { // actual addition of rhs to *this return *this; } }; inline X operator+(X lhs, const X rhs) { lhs += rhs; return lhs; } 

operator+= onun nəticəsini istinad edərək qaytarır və operator+ nəticəsinin surətini qaytarır. Əlbəttə, bir linkə qayıtmaq surətini təkrar qaytarmaqdan daha səmərəlidir, amma operator+ halda operator+ surətini çıxarmaq üçün heç bir yol yoxdur. a + b yazdığınız zaman nəticənin yeni bir dəyər olmasını gözlədiyiniz üçün operator+ yeni bir dəyər qaytarmalıdır. 3 Həmçinin qeyd edək ki, operator+ sol operandı daimi bir istinad kimi yox, bir surət kimi qəbul edir. Bunun səbəbi operator= onun argümanı bir surət kimi qəbul edildiyini göstərir ki, bunun səbəbidir.

Bit manipulyasiya operatorları ~ | ^ << >> aritmetik operatorlarla eyni şəkildə həyata keçirilməlidir. Bununla belə (çıxış və giriş üçün <<>> yüklənmə istisna olmaqla) aşırı yükləmə üçün kifayət qədər az istifadə olunur.

3 Bununla yanaşı, öyrənilən dərsdir ki, a += b ümumiyyətlə a + b dən daha təsirli olur və mümkün olduqda üstünlük təşkil edilməlidir.

Array counting

Serial indeksi operatoru sinif üzvü olaraq tətbiq edilməli olan ikili operatordur. Data elementlərinizə bir əsasla daxil olmaq üçün imkan verən konteyner növləri üçün istifadə olunur. Bunların qaynaq forması aşağıdakı kimidir:

 class X { value_type operator[](index_type idx); const value_type operator[](index_type idx) const; // ... }; 

Sınıfınızın istifadəçiləri operator[] tərəfindən qaytarılan məlumatların elementlərini dəyişdirməsini istəmirsinizsə (bu halda, qeyri-daimi seçimi ləğv edə bilərsiniz), həmişə operatorun hər iki versiyasını da təqdim etməlisiniz.

Value_type növü daxili tipə aiddirsə, operatorun const versiyası const istinadından bir surəti qaytarmalıdır.

Göstərici tipləri üçün operatorlar

Öz yineleyicilərinizi və ya ağıllı göstəricilərinizi müəyyən etmək üçün unary prefiqs markup operatorunu * və giriş operatorunu ikili göstərici üzvünün infix -> :

 class my_ptr { value_type operator*(); const value_type operator*() const; value_type* operator->(); const value_type* operator->() const; }; 

Xahiş edirik unutmayın ki, həm də həmişə bir const və qeyri-daimi versiyasını həmişə tələb edirlər. Operator üçün -> value_type növü class (və ya struct və ya union ), başqa operator->() recursively deyilir, operator->() isə qeyri-sinf dəyərini qaytarmır.

Unary ünvanı-operator heç vaxt yüklənməməlidir.

operator->*() bu suala baxın. Nadir hallarda istifadə edilir və bu səbəblə nadir hallarda çox yüklənir. Əslində, hətta iteratorlar da onu yükləməzlər.


Dönüşüm operatorlarına davam edin

945
12 дек. cavab sbi 12 dekabr verilir . 2010-12-12 15:47 '10 at 15:47 2010-12-12 15:47

C ++ operator yüklənmə üçün üç əsas qaydalar

C ++ sistemində operator yüklənməsinə gəldikdə, təqib etdiyiniz üç əsas qaydalar var. Bütün bu qaydalara baxmayaraq, həqiqətən istisnalar var. Bəzən insanlar onlardan kənara çıxdılar və nəticə yaxşı bir kod idi, amma bu cür müsbət dəyişikliklər çox az və uzaqdır. Gördüyüm bu 100 anormallikdən ən az 99u əsassız idi. Lakin 1000-dən 999-a kimi ola bilər. Beləliklə, aşağıdakı qaydalara riayət edin.

  • Operatorun mənası aydın olmadıqda və inkar olunmazsa, çox yüklənməməlidir. Bunun əvəzinə yaxşı seçilmiş bir adla funksiyanı təmin edin. Prinsipcə, operatorun aşırı yüklənməsinin ilk və ən vacib qaydası, ürəyində belə deyir: "Bunu etməyin. Bu, qəribə görünə bilər, çünki operatorun yüklənməsi barədə çox məlumat var və buna görə bir çox məqalələr, kitab fəsilləri və digər mətnlər bunlarla bağlıdır. Lakin bu görünən açıq-aydın sübutlara baxmayaraq, operatorun yüklənməsinin uyğun olduğu yalnız təəccüblü az hallar mövcuddur. Bunun səbəbi operatorun tətbiqinin əsasını təşkil edən semantikləri anlamaq çətindir. ene proqram tanınmış və mübahisəsiz deyil. Məşhur inam əksinə, bu demək olar ki, heç vaxt haldır.

  • Həmişə tanınmış semantik operatorlarına riayət edin.
    C ++ artıq yüklənən operatorların semantiklərinə məhdudiyyət qoymur. Sizin kompilyator məmnuniyyətlə sağ əl operatorunu çıxarmaq üçün ikili + operatoru tətbiq edən bir kod qəbul edəcəkdir. Bununla belə, belə bir operatorun istifadəçiləri a + b ifadəsindən a + b çıxmaq üçün heç bir şübhə etməyəcəklər. Əlbəttə ki, bu, tətbiq sahəsindəki operatorun semantikasının inkar edilə bilməz olduğunu düşünür.

  • Həmişə əlaqədar əməliyyatlar bütün set təmin edir.
    Operatorlar bir-biri ilə və digər əməliyyatlarla əlaqələndirilir. Sizin tipiniz a + b ni dəstəkləyirsə, istifadəçilər də a += b deyə bilərlər. ++a prefiksini artırdığını dəstəkləyərsə, onlar da a++ işləməsini gözləyirlər. a < b olub-olmadığını yoxlaya bilsələr, ehtimal ki, onlar da varmı? Onlar növü kopyalayıblarsa, tapşırığın da işləməsini gözləyirlər.

border=0

Üzv və qeyri-üzv arasında qərar verməyə davam edin.

459
12 дек. cavab sbi 12 dekabr verilir . 2010-12-12 15:45 '10 saat 15:45 'də 2010-12-12 15:45

C ++-da operator yüklənmə üçün ümumi sintaksis

Operatorların C ++-da quraşdırılmış növlər üçün dəyərini dəyişdirə bilməzsiniz, operatorlar yalnız xüsusi növlər üçün 1-dən çox yüklənə bilər. Yəni operandlardan ən az bir istifadəçi müəyyən bir növü olmalıdır. Digər yüklənmiş funksiyaları olduğu kimi, operatorlar müəyyən bir parametr dəsti üçün yalnız bir dəfə yüklənə bilər.

Bütün operatorlar C ++-da çox yüklənə bilməz. Aşırı yüklənə bilməyən operatorlar arasında :. :: sizeof typeid .* və C ++ da yeganə üçqat operator ?:

C ++-da yüklənə biləcək operatorlar arasında aşağıdakılar var:

  • aritmetik operatorlar: + - * / %+= -= *= /= %= (bütün ikili infix); + - (unary prefix); ++ -- (unary prefiks və postfiks)
  • bit manipulyasiyası: > | ^ << >>> |= ^= <<= >>= (bütün ikili infix); ~ (unary prefiksi)
  • Boole cədvəli: == != < > <= >= || > (bütün ikili infix); ! (unary prefix)
  • Yaddaş idarə: new new[] delete delete[]
  • Hərəkətsiz dönüşüm operatorları
  • toplama: = [] -> ->* , (bütün ikili infix); * > (bütün unary prefix) () (funksiya çağırışı, n-ary infix)

Bununla belə, bütün bunları həddindən artıq yükləyə bilərsiniz ki, bunu etməlisiniz. Əsas Operatorun Aşırı yükləmə qaydalarına baxın.

C ++-da operatorlar xüsusi adlar olan funksiyalar kimi çox yüklənirlər. Digər funksiyalarda olduğu kimi, həddən artıq yüklənmiş operatorlar adətən , onların sol işlənmiş elementlərinin funksiyası və yaxud üzv olmayan funksiyalar kimi həyata keçirilə bilər. Seçdiyiniz və ya onlardan birini istifadə edə biləcəyinizdən asılı olmayaraq, bir neçə kriterdən asılıdır. 2 x obyektinə tətbiq olunan unary operator @ ya operator@(x) və ya x.operator@() kimi x.operator@() . xy obyektlərinə tətbiq olunan ikili infix operatoru ya operator@(x,y) və ya x.operator@(y) . 4

Üzv olmayan funksiyalar kimi tətbiq olunan operatorlar bəzən öz növlərinin operandlarıdır.

1 "istifadəçi müəyyən" termini bir az yanlış ola bilər. C ++ daxili növləri və istifadəçi tipləri arasında fərq qoyur. Birincisi, məsələn, int, char və ikiqat daxildir; ikincisi, standart kütüphaneden, o cümlədən istifadəçilər tərəfindən müəyyən edilməmiş olsa belə, bütün növ strukturları, sinfi, birliyini və sayımını əhatə edir.

2 Bu , bu Sualın son hissəsində təsvir olunur.

3 @ etibarlı C ++ operatoru deyil, mən bunu bir yer tutucu kimi istifadə edirəm.

4 C ++-da vahid üçlü operator çox yüklənə bilməz və bir n-ary operatoru həmişə üzv funksiyası kimi tətbiq edilməlidir.


C ++-da operator yüklənmə üçün üç əsas qaydalara davam edin.

242
12 дек. cavab sbi 12 dekabr verilir . 2010-12-12 15:46 '10 at 15:46 2010-12-12 15:46

Üzv və üzv olmayan üzvlər arasında qərar

İkili operatorlar = (tapşırıq), [] (seriallara abunə olmaq), -> (üzv olmaq) və n-ary () operatoru (funksiya çağırışı) həmişə üzv funksiyaları kimi həyata keçirilməlidir, çünki onlar dilin sintaksisini tələb edirlər .

Digər operatorlar yaxud üzv kimi və ya qeyri-üzv ola bilməzlər. Onların bəziləri, adətən, qeyri-üzv funksiyaları kimi həyata keçirilməlidir, çünki sol operand sizin tərəfindən dəyişdirilə bilməz. Bunlardan ən çox diqqətəlayiq olan, sol əl operatorları standart kitabxanadan dəyişdirə bilmədiyiniz axın sinfləri olan <<>> giriş və çıxış operatorlarıdır.

Для всех операторов, где вам нужно либо реализовать их как функцию-член, либо не-членную функцию, использовать следующие правила большого пальца :

  • Если это унарный оператор , реализуйте его как функцию member .
  • Если двоичный оператор обрабатывает оба операнда одинаково (он оставляет их неизменными), реализуйте этот оператор как функцию не-член .
  • Если двоичный оператор не обрабатывает оба его операнда равно (обычно это изменяет его левый операнд), может быть полезно сделать его член функции его левого операнда типа, если он должен получить доступ к частным частям операнда.

Конечно, как и во всех эмпирических правилах, есть исключения. Если у вас есть тип

 enum Month {Jan, Feb, ..., Nov, Dec} 

и вы хотите перегрузить операторы инкремента и декремента для него, вы не можете делать это как функции-члены, поскольку в С++ типы перечислений не могут иметь функции-члены. Поэтому вам нужно перегрузить его как бесплатную функцию. И operator<() для шаблона класса, вложенного в шаблон класса, гораздо проще писать и читать, когда выполняются как члены-члены inline в определении класса. Но это действительно редкие исключения.

(Тем не менее, если вы делаете исключение, не забывайте о проблеме const -ness для операнда, который для функций-членов становится неявным аргументом this . Если оператор как функция, не являющаяся членом, возьмите его левый аргумент как ссылку const , тот же оператор, что и функция-член, должен иметь const в конце, чтобы сделать ссылку *this a const .)


Продолжить Общие операторы для перегрузки .

223
ответ дан sbi 12 дек. '10 в 15:49 2010-12-12 15:49

Операторы преобразования (также известные как пользовательские преобразования)

В С++ вы можете создавать операторы преобразования, операторы, которые позволяют компилятору конвертировать между вашими типами и другими определенными типами. Существует два типа операторов преобразования: неявные и явные.

Неявные операторы преобразования (С++ 98/С++ 03 и С++ 11)

Оператор неявного преобразования позволяет компилятору неявно преобразовывать (например, преобразование между int и long ) значение определенного пользователем типа в другой тип.

Ниже приведен простой класс с неявным оператором преобразования:

 class my_string { public: operator const char*() const {return data_;} // This is the conversion operator private: const char* data_; }; 

Неявные операторы преобразования, такие как конструкторы с одним аргументом, являются пользовательскими преобразованиями. Компиляторы будут предоставлять одно пользовательское преобразование при попытке совпадения вызова с перегруженной функцией.

 void f(const char*); my_string str; f(str); // same as f( str.operator const char*() ) 

Сначала это кажется очень полезным, но проблема заключается в том, что неявное преобразование даже срабатывает, когда оно не ожидается. В следующем коде void f(const char*) будет вызываться, потому что my_string() не является lvalue , поэтому первое не соответствует:

 void f(my_string void f(const char*); f(my_string()); 

Начинающие легко ошибаются, и даже опытные программисты на C++ иногда удивляются, потому что компилятор выбирает перегруз, который они не подозревали. Эти проблемы могут быть смягчены явными операторами преобразования.

Явные операторы преобразования (С++ 11)

В отличие от операторов неявного преобразования, явные операторы преобразования никогда не будут использовать, когда вы этого не ожидаете. Ниже приведен простой класс с явным оператором преобразования:

 class my_string { public: explicit operator const char*() const {return data_;} private: const char* data_; }; 

Обратите внимание на explicit . Теперь, когда вы пытаетесь выполнить неожиданный код из операторов неявного преобразования, вы получаете ошибку компилятора:

prog.cpp: In function 'int main()':prog.cpp:15:18: error: no matching function for call to 'f(my_string)'prog.cpp:15:18: note: candidates are:prog.cpp:11:10: note: void f(my_stringprog.cpp:11:10: note: no known conversion for argument 1 from 'my_string' to 'my_stringprog.cpp:12:10: note: void f(const char*)prog.cpp:12:10: note: no known conversion for argument 1 from 'my_string' to 'const char*'

Чтобы вызвать явный оператор литья, вы должны использовать static_cast , листинг в стиле C или стиль стиля конструктора (т.е. T(value) ).

Однако есть одно исключение: компилятору разрешено неявно преобразовывать в bool . Кроме того, компилятору не разрешается выполнять другое неявное преобразование после преобразования в bool (компилятору разрешено выполнять 2 неявных преобразования за раз, но только 1 пользовательское преобразование при макс.).

Поскольку компилятор не будет передавать "прошлый" bool , явные операторы преобразования теперь устраняют необходимость Safe Bool idiom . Например, интеллектуальные указатели до С++ 11 использовали идиому Safe Bool, чтобы предотвратить конверсию в интегральные типы. В С++ 11 интеллектуальные указатели используют явный оператор вместо этого, потому что компилятору не разрешается неявно преобразовывать его в интегральный тип после того, как он явно преобразовал тип в bool.

Продолжить Перегрузка new и delete .

150
ответ дан JKor 17 мая '13 в 21:32 2013-05-17 21:32

Перегрузка new и delete

Примечание. . Это относится только к синтаксису перегрузки new и delete , а не к реализации таких перегруженных операторов. Я думаю, что семантика перегрузки new и delete заслуживает собственных FAQ , в рамках темы перегрузки оператора я никогда не смогу сделать это справедливость.

Əsaslar

В С++, когда вы пишете новое выражение , например new T(arg) , при вычислении этого выражения происходит две вещи: сначала вызывается operator new для получения необработанной памяти, и затем вызывается соответствующий конструктор T , чтобы превратить эту необработанную память в действительный объект. Аналогично, когда вы удаляете объект, сначала вызывается его деструктор, а затем память возвращается в operator delete .
С++ позволяет вам настраивать обе эти операции: управление памятью и строительство/уничтожение объекта в выделенной памяти. Последнее делается путем написания конструкторов и деструкторов для класса. Управление точной настройкой памяти выполняется путем написания собственных operator new и operator delete .

Первое из основных правил перегрузки оператора - не делайте этого - применяется, в частности, для перегрузки new и delete . Почти единственными причинами перегрузки этих операторов являются проблемы с производительностью и ограничения памяти , и во многих случаях другие действия, такие как изменения используемых алгоритмов, обеспечат значительно более высокая стоимость/коэффициент усиления , чем попытка настройки управления памятью.

Стандартная библиотека С++ поставляется с набором предопределенных операторов new и delete . Самые важные из них:

 void* operator new(std::size_t) throw(std::bad_alloc); void operator delete(void*) throw(); void* operator new[](std::size_t) throw(std::bad_alloc); void operator delete[](void*) throw(); 

Первые два выделяют/освобождают память для объекта, а последние два - для массива объектов. Если вы предоставите свои собственные версии, они будут не перегружать, а заменяют те из стандартной библиотеки.
Если вы перегружаете operator new , вы всегда должны также перегружать соответствие operator delete , даже если вы никогда не собираетесь его называть. Причина в том, что если конструктор бросает во время оценки нового выражения, система времени выполнения вернет память в operator delete , соответствующую operator new , который был вызван, чтобы выделить память для создания объекта. Если вы не предоставляете соответствие operator delete , по умолчанию вызывается, что почти всегда неверно.
Если вы перегружаете new и delete , вы также должны перегрузить варианты массивов.

Размещение new

С++ позволяет операторам new и delete принимать дополнительные аргументы. Так называемое размещение new позволяет вам создать объект по определенному адресу, который передается:

 class X {  }; char buffer[ sizeof(X) ]; void f() { X* p = new(buffer) X(); // ... p->~X(); // call destructor } 

Стандартная библиотека поставляется с соответствующими перегрузками для новых и удаленных операторов:

 void* operator new(std::size_t,void* p) throw(std::bad_alloc); void operator delete(void* p,void*) throw(); void* operator new[](std::size_t,void* p) throw(std::bad_alloc); void operator delete[](void* p,void*) throw(); 

Обратите внимание, что в примере кода для размещения нового, указанного выше, operator delete никогда не вызывается, если только конструктор X не выбрасывает исключение.

Вы также можете перегрузить new и delete другими аргументами. Как и в случае дополнительного аргумента для размещения new, эти аргументы также перечисляются в круглых скобках после ключевого слова new . Просто по историческим причинам такие варианты часто также называют размещением нового, даже если их аргументы не предназначены для размещения объекта по определенному адресу.

Новый класс и удаление

Чаще всего вы захотите точно настроить управление памятью, поскольку измерение показало, что экземпляры определенного класса или группы связанных классов создаются и уничтожаются часто и что управление памятью по умолчанию для системы времени выполнения, настроенный на общую производительность, неэффективно работает в этом конкретном случае. Чтобы улучшить это, вы можете перегрузить новые и удалить для определенного класса:

 class my_class { public: // ... void* operator new(); void operator delete(void*,std::size_t); void* operator new[](size_t); void operator delete[](void*,std::size_t); // ... }; 

Перегруженные таким образом, новые и удаленные ведут себя как статические функции-члены. Для объектов my_class аргумент std::size_t всегда будет sizeof(my_class) . Однако эти операторы также вызываются для динамически выделенных объектов производных классов , и в этом случае он может быть больше этого.

Глобальное новое и удаление

Чтобы перегрузить глобальное новое и удалить, просто замените предварительно определенные операторы стандартной библиотеки на собственные. Однако это редко нужно делать.

137
ответ дан sbi 12 дек. '10 в 16:07 2010-12-12 16:07

Почему функция operator<< для потоковой передачи объектов в std::cout или в файл не является функцией-членом?

Скажем, у вас есть:

 struct Foo { int a; double b; std::ostream operator<<(std::ostream out) const { return out << a << " " << b; } }; 

Учитывая, что вы не можете использовать:

 Foo f = {10, 20.0}; std::cout << f; 

Так как operator<< перегружается как функция-член от Foo , LHS оператора должен быть Foo объектом. Это означает, что вам необходимо будет использовать:

 Foo f = {10, 20.0}; f << std::cout 

что очень неинтуитивно.

Если вы определяете его как функцию, не являющуюся членом,

 struct Foo { int a; double b; }; std::ostream operator<<(std::ostream out, Foo const f) { return out << fa << " " << fb; } 

Вы сможете использовать:

 Foo f = {10, 20.0}; std::cout << f; 

который очень интуитивно понятен.

34
ответ дан R Sahu 22 янв. '16 в 22:00 2016-01-22 22:00

Другие вопросы по меткам или Задайте вопрос