c++ شرح - كيف يمكنك ضبط ومسح وتبديل قطعة واحدة؟




bitwise operators (22)

من المفيد أحيانًا استخدام enum لتسمية البتات:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

ثم استخدم الأسماء في وقت لاحق. أي أكتب

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

لتعيين واضحة والاختبار. بهذه الطريقة تخفي الأرقام السحرية عن بقية الشفرة.

بخلاف ذلك أؤيد حل جيريمي.

كيف يمكنك ضبط ، ومسح ، وتبديل بعض الشيء في C / C ++؟


استخدم هذا:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

استخدم معاملات البتات: & |

لتعيين آخر بت في 000b :

foo = foo | 001b

للتحقق من البت الأخير في foo :

if ( foo & 001b ) ....

لمسح آخر بت في foo :

foo = foo & 110b

لقد استخدمت XXXb أجل الوضوح. من المحتمل أنك ستعمل مع تمثيل HEX ، بناءً على بنية البيانات التي تقوم بتغليف البت فيها.


كيف يمكنك ضبط ومسح وتبديل قطعة واحدة؟

لمعالجة مشكلة الترميز الشائعة عند محاولة تشكيل القناع:
1ليس دائمًا واسعًا بما فيه الكفاية

ما هي المشاكل التي تحدث عندما numberيكون نوع أوسع من 1؟
xقد يكون كبيرًا جدًا بالنسبة للتحول الذي 1 << xيؤدي إلى سلوك غير معروف (UB). حتى لو xلم يكن كبيرًا جدًا ، ~فقد لا يتجاهل أجزاءًا أكثر أهمية.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

لضمان 1 واسعة بما فيه الكفاية:

يمكن أن تستخدم التعليمات البرمجية 1ullأو بشكل توضيحي (uintmax_t)1والسماح للمجمع بتحسين.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

أو يلقي - مما يجعل لمسائل الترميز / المراجعة / الصيانة الحفاظ على فريق العمل الصحيح والحديث.

number |= (type_of_number)1 << x;

أو رقي بلطف عن 1طريق إجبار عملية الرياضيات التي هي على الأقل واسعة مثل نوع number.

number |= (number*0 + 1) << x;

كما هو الحال مع معظم عمليات التلاعب في البتات ، من الأفضل العمل مع أنواع غير موقعة بدلاً من تلك الموقعة


هذا البرنامج هو تغيير أي بت البيانات من 0 إلى 1 أو 1 إلى 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

أستخدم وحدات الماكرو المحددة في ملف رأس للتعامل مع مجموعة البت ومسحها:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

نهج bitfield له مزايا أخرى في الساحة المضمنة. يمكنك تحديد بنية تقوم بتعيينها مباشرة على وحدات البت في سجل أجهزة معين.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

يجب أن تكون على دراية بأمر تعبئة البت - أعتقد أنه MSB أولاً ، ولكن قد يكون هذا معتمدًا على التنفيذ. أيضاً ، تحقق من كيفية عبارات معالجات برنامج التحويل البرمجي عبور حدود البايت.

يمكنك بعد ذلك القراءة والكتابة واختبار القيم الفردية كما كان من قبل.


التوسيع على bitsetالجواب:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

تحقق قليلا في مكان عشوائي في متغير من النوع التعسفي:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

استخدام العينة:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

ملاحظات: تم تصميم هذا ليكون سريعًا (نظراً بمرونته) وغير فرعي. ينتج عنه رمز آلة SPARC فعال عند تجميع Sun Studio 8؛ لقد اختبرت ذلك أيضا باستخدام MSVC ++ 2008 على amd64. من الممكن إنشاء وحدات ماكرو مماثلة لإعداد وتصفية البت. الاختلاف الرئيسي في هذا الحل مقارنة بالعديد من الحلول الأخرى هنا هو أنه يعمل لأي موقع في أي نوع من المتغيرات.


بالنسبة للمبتدئين ، أود أن أشرح أكثر قليلاً بمثال:

مثال:

value is 0x55;
bitnum : 3rd.

يتم استخدام المشغل & التحقق من البت:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

تبديل أو انعكاس:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| المشغل: ضبط بت

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

Visual C 2010 ، وربما العديد من المجمعين الآخرين ، لديهم دعم مباشر لعمليات بت المضمنة. والمثير للدهشة ، وهذا يعمل ، حتى يعمل مشغل sizeof () بشكل صحيح.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

إذاً ، بالنسبة لسؤالك ، فإن IsGph [i] = 1 ، أو IsGph [i] = 0 يجعل الإعداد ومسح bools أمرًا سهلاً.

للعثور على أحرف غير قابلة للطباعة ...

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

لاحظ أنه لا يوجد شيء "خاص" حول هذا الرمز. يعامل قليلا مثل عدد صحيح - من الناحية الفنية ، هو. عدد صحيح 1 بت يمكنه الاحتفاظ بقيمتين وقيمتين فقط.

استخدمت مرة واحدة هذا النهج للعثور على سجلات القروض المكررة ، حيث كان loan_number مفتاح ISAM ، وذلك باستخدام رقم القرض المكون من 6 أرقام كمؤشر في مجموعة بت. صوم بوحشية ، وبعد 8 أشهر ، أثبت أن نظام حاسب مركزي كنا نحصل على البيانات منه كان في الواقع غير صحيح. إن بساطة صفائف البت تجعل الثقة في صحتها عالية جداً - مقارنةً بنهج البحث على سبيل المثال.


باستخدام مكتبة C ++ القياسية: std::bitset<N> .

أو الإصدار Boost : boost::dynamic_bitset .

ليست هناك حاجة لللف الخاصة بك:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010

يسمح إصدار Boost بتقطيع حجم وقت التشغيل مقارنةً بمجموعة bitset في وقت تجميع المكتبة القياسية .


هنا هو المفضل الخاص بي الماكرو حسابية ، الذي يعمل لأي نوع من صفيف صحيح غير موقعة من unsigned char إلى size_t (وهو أكبر نوع يجب أن تكون فعالة للعمل مع):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

لتعيين قليلا:

BITOP(array, bit, |=);

لمسح قليلا:

BITOP(array, bit, &=~);

لتبديل بعض الشيء:

BITOP(array, bit, ^=);

لاختبار قليلا:

if (BITOP(array, bit, &)) ...

إلخ


الخيار الآخر هو استخدام حقول البت:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

يحدد مجال 3 بت (في الواقع ، انها ثلاث اللحامات 1 بت). أصبحت عمليات البت الآن قليلا (haha) أبسط:

لتعيين أو مسح بعض الشيء:

mybits.b = 1;
mybits.c = 0;

لتبديل بعض الشيء:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

التحقق قليلا:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

هذا يعمل فقط مع حقول بت حجم ثابت. خلاف ذلك ، يجب عليك اللجوء إلى تقنيات التلاعب قليلاً الموضحة في المشاركات السابقة.


أكثر عمومية ، للصور النقطية ذات الحجم التعسفي:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

int set_nth_bit(int num, int n){

    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){

    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){

    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){

    return num & (1 << n);
}

من snip-c.zip 's bitops.h:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

حسنًا ، دعنا نحلل الأشياء ...

التعبير الشائع الذي يبدو أنك تواجه مشاكل فيه في كل هذه "(1L << (posn))". كل هذا يفعل هو خلق قناع مع بت واحد على والتي ستعمل مع أي نوع صحيح. تحدد الوسيطة "posn" الموضع الذي تريد فيه البت. إذا كانت posn == 0 ، فسيتم تقييم هذا التعبير إلى:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

إذا posn == 8 ، فإنه سيتم تقييمه

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

وبعبارة أخرى ، فإنه ينشئ ببساطة حقل من 0 مع 1 في الموضع المحدد. الجزء الوحيد صعب في الماكرو BitClr () حيث نحتاج إلى تعيين بت 0 واحد في حقل 1. يتم إنجاز ذلك باستخدام مكمل 1 من نفس التعبير كما هو مشار إليه بواسطة عامل التلدة (~).

بمجرد إنشاء القناع يتم تطبيقه على الوسيطة كما تقترح ، من خلال استخدام معامل البتات و (&) ، أو (|) ، و xor (^). نظرًا لأن القناع من النوع الطويل ، فإن وحدات الماكرو ستعمل أيضًا على شارات char أو short's أو int أو longs.

خلاصة القول هي أن هذا هو الحل العام لفئة كاملة من المشاكل. وبالطبع ، من الممكن بل ومن المناسب إعادة كتابة ما يعادل أي من وحدات الماكرو هذه بقيم قناع صريحة في كل مرة تحتاج فيها ، ولكن لماذا تفعل ذلك؟ تذكر أن استبدال الماكرو يحدث في المعالج الأولي ، وبالتالي فإن الشفرة التي تم توليدها ستعكس حقيقة أن القيم تعتبر ثابتة من قبل المترجم - أي أنه بنفس كفاءة استخدام وحدات الماكرو العامة "لإعادة اختراع العجلة" في كل مرة تحتاج فيها إلى تفعل قليلا التلاعب.

غير مقتنع؟ إليك بعض كود الاختبار - لقد استخدمت Watcom C مع التحسين الكامل وبدون استخدام _cdecl لذا سيكون التفكيك الناتج نظيفًا قدر الإمكان:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (مفكك)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------


إذا كنت ترغب في تنفيذ جميع هذه العمليات مع البرمجة C في نواة لينكس ، فأقترح استخدام واجهات برمجة التطبيقات القياسية لنواة لينكس.

انظر https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

ملاحظة: هنا العملية بأكملها تحدث في خطوة واحدة. لذلك ، يتم ضمان جميع هذه الأجهزة أن تكون ذرية حتى في أجهزة الكمبيوتر التي تعمل بنظام SMP ، كما أنها مفيدة للحفاظ على الاتساق عبر المعالجات.


فيما يلي بعض وحدات الماكرو التي أستخدمها:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

وضع قليلا

استخدم عامل التشغيل OR بالبت ( | ) لتعيين بعض الشيء.

number |= 1UL << n;

سيؤدي ذلك إلى تحديد n البتات. يجب أن يكون n صفراً ، إذا كنت ترغب في ضبط 1 bit وهكذا على u n-1 ، إذا كنت تريد ضبط البتة th.

استخدم 1ULL إذا كان number أكبر من unsigned long ؛ لا يتم الترويج لـ 1UL << n حتى بعد تقييم 1UL << n حيث يتم تغيير السلوك غير المحدد بأكثر من عرض long . وينطبق الشيء نفسه على كل ما تبقى من الأمثلة.

تطهير قليلا

استخدم عامل التشغيل AND AND (لإلغاء مسح).

number &= ~(1UL << n);

سيؤدي ذلك إلى مسح الجزء التاسع من number . يجب عكس سلسلة البت مع عامل التشغيل NOT bitwise ( ~ ) ، ثم AND.

تبديل قليلا

يمكن استخدام عامل التشغيل XOR ( ^ ) للتبديل بعض الشيء.

number ^= 1UL << n;

سيؤدي ذلك إلى تبديل الجزء n من number .

التحقق قليلا

أنت لم تسأل عن هذا ، لكن قد أضيفه أيضًا.

للتحقق من بعض الشيء ، قم برفع الرقم n إلى اليمين ، ثم إلى bitwise AND:

bit = (number >> n) & 1U;

سيضع ذلك قيمة البتة number من number في bit المتغير.

تغيير البتة th إلى x

يمكن تحقيق ضبط البتة n إلى 1 أو 0 مع ما يلي في تنفيذ C ++ المتمم 2:

number ^= (-x ^ number) & (1UL << n);

سيتم ضبط البت n إذا كان x 1 ، ويتم مسحه إذا كانت x 0 . إذا كان لدى x قيمة أخرى ، فستحصل على القمامة. x = !!x سيصبح منطقية إلى 0 أو 1.

لجعل هذا الأمر مستقلاً عن سلوك الإبطال المكمّل في 2 (حيث -1 تم تعيين كل البتات ، على خلاف التكملة 1 أو توقيع / حجم تطبيق C ++) ، استخدم نفي غير موقع.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

أو

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

من المفيد عمومًا استخدام أنواع غير الموقعة من أجل معالجة البتات المحمولة.

من المفيد أيضًا بشكل عام عدم نسخ / لصق الكود بشكل عام والكثير من الأشخاص يستخدمون وحدات الماكرو preprocessor (مثل إجابة wiki للمنتدى باستمرار ) أو نوعًا ما من التغليف.


نظرًا لأنه تم وضع علامة على هذا "مضمّن" ، أفترض أنك تستخدم وحدة تحكم دقيقة. جميع الاقتراحات المذكورة أعلاه صالحة وتعمل (اقرأ -تعديل- الكتابة ، والنقابات ، والبنى ، وما إلى ذلك).

ومع ذلك ، خلال نوبة من تصحيح الأخطاء المستندة إلى الذبذبات ، دهشت عندما اكتشفت أن هذه الطرق لها تأثير كبير في دورات وحدة المعالجة المركزية مقارنة بكتابة قيمة مباشرة إلى سجلات PORTnSET / PORTnCLEAR الصغرى التي تحدث فرقًا حقيقيًا عندما تكون هناك حلقات ضيقة / عالية التردد دبابيس ISR ل.

بالنسبة لأولئك غير المألوفين: في المثال الخاص بي ، يحتوي الصغير على سجل PURN عام لحالة دبوس والذي يعكس دبابيس الإخراج ، وبذلك يتم تنفيذ PORTn | = BIT_TO_SET في القراءة - التعديل - الكتابة إلى هذا السجل. ومع ذلك ، تأخذ سجلات PORTnSET / PORTnCLEAR "1" ليعني "الرجاء جعل هذه البتة 1" (SET) أو "الرجاء جعل هذا بت صفر" (CLEAR) و "0" تعني "ترك الدبوس وحده". لذلك ، ينتهي بك الأمر مع اثنين من عناوين المنفذ اعتمادًا على ما إذا كنت تقوم بتحديد أو مسح البت (غير مناسب دائمًا) ولكن رد فعل أسرع بكثير وتعليمة برمجية أصغر حجمًا.


هناك بعض الإجابات الجيدة بالفعل. سأركز بشكل رئيسي على ما أعتقد أنهم يفتقرون إليه - تفسير "السلبيات" مع لغة النسخ والتقليب ....

ما هي لغة النسخة والمبادلة؟

طريقة لتنفيذ مشغل التعيين من حيث وظيفة المبادلة:

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

الفكرة الأساسية هي:

  • الجزء الأكثر عرضة للخطأ من التخصيص إلى كائن هو ضمان أي موارد يتم الحصول عليها احتياجات الدولة الجديدة (مثل الذاكرة ، واصفات)

  • يمكن محاولة هذا الاكتساب قبل تعديل الحالة الحالية للكائن (أي *this ) إذا تم عمل نسخة من القيمة الجديدة ، وهذا هو السبب في قبول rhs بالقيمة (أي منسوخة) بدلاً من الرجوع إليها

  • تبديل حالة نسخ rhs المحلية و *this عادة ما يكون من السهل نسبيا القيام به دون الفشل / الاستثناءات المحتملة ، بالنظر إلى أن النسخة المحلية لا تحتاج إلى أي حالة معينة بعد ذلك (تحتاج فقط إلى حالة مناسبة لتشغيل destructor ، بقدر ما ل كائن يتم نقله من في> = C ++ 11)

متى يجب استخدامها؟ (ما هي المشاكل التي تحلها [/ create] ؟)

  • عندما تريد أن يعترض المعترض غير متأثر بمهمة تطرح استثناءً ، بافتراض أن لديك أو يمكن أن تكتب swap مع ضمان استثناء قوي ، ومن الناحية المثالية لا يمكن للفشل / throw .. †

  • عندما تريد طريقة نظيفة وسهلة الفهم ، قوية لتعريف مشغل المهمة من حيث (أبسط) نسخ المنشئ ، وظائف swap و destructor.

    • إن تخصيص المهام الذاتية كنسخة وتقليب يتجنب حالات الحواف التي لا يتم إغفالها.

  • عندما تكون أي عقوبة أداء أو استخدام أعلى للموارد بشكل مؤقت يتم إنشاؤه من خلال وجود كائن مؤقت إضافي أثناء المهمة غير مهم للتطبيق الخاص بك. ⁂

† رمي swap : من الممكن بصفة عامة تبادل أعضاء البيانات بطريقة موثوقة ، بحيث تتتبع الكائنات بواسطة المؤشر ، ولكن أعضاء البيانات غير المؤشرين الذين لا يملكون مبادلة خالية من الإلقاء ، أو يجب تنفيذ التبادل مثل X tmp = lhs; lhs = rhs; rhs = tmp; X tmp = lhs; lhs = rhs; rhs = tmp; وقد يتم رمي الإنشاء أو الإنشاء أو التنازل ، ولكن لا يزال هناك احتمال للفشل في ترك بعض بيانات الأعضاء المتبادلة وغيرهم. تنطبق هذه الإمكانية حتى على std::string في C ++ 03 كما يعلق James على إجابة أخرى:

wilhelmtell: في C ++ 03 ، لا يوجد أي ذكر من الاستثناءات المحتمل طرحها بواسطة std :: string :: swap (الذي يسمى بواسطة std :: swap). في C ++ 0x، std :: string :: swap هو noexcept ويجب عدم إلقاء الاستثناءات. - جيمس ماكنيلس في 22 ديسمبر 2010 في تمام الساعة 15:24

‡ تنفيذ مشغل التعيين الذي يبدو عاقلًا عند التعيين من كائن مميز يمكن أن يفشل بسهولة للتخصيص الذاتي. في حين أنه قد يبدو من الصعب تخيل أن شفرة العميل ستحاول حتى تعيين الذات ، إلا أنه يمكن أن يحدث بسهولة نسبية أثناء عمليات algo على الحاويات ، مع x = f(x); رمز حيث f (ربما لبعض الفروع #ifdef ) ماكرو ala #define f(x) x أو دالة بإرجاع مرجع إلى x أو حتى (على الأرجح غير فعالة ولكن موجزة) رمز مثل x = c1 ? x * 2 : c2 ? x / 2 : x; x = c1 ? x * 2 : c2 ? x / 2 : x; ). فمثلا:

struct X
{
    T* p_;
    size_t size_;
    X& operator=(const X& rhs)
    {
        delete[] p_;  // OUCH!
        p_ = new T[size_ = rhs.size_];
        std::copy(p_, rhs.p_, rhs.p_ + rhs.size_);
    }
    ...
};

في التعيين الذاتي ، حذف الرمز أعلاه x.p_; نقاط p_ في منطقة الكومة المخصصة حديثًا ، ثم يحاول قراءة البيانات غير p_ في ذلك (السلوك غير المحدد) ، إذا كان ذلك لا يفعل أي شيء غريبًا للغاية ، فإن copy تحاول إجراء تكليف ذاتي لكل T فقط!

⁂ يمكن لنسخة النسخة والمبادلة أن تُظهر أوجه قصور أو قيود بسبب استخدام مؤقت إضافي (عندما تكون معلمة المشغل هي نسخة مبنية):

struct Client
{
    IP_Address ip_address_;
    int socket_;
    X(const X& rhs)
      : ip_address_(rhs.ip_address_), socket_(connect(rhs.ip_address_))
    { }
};

هنا ، قد يتحقق Client::operator= مكتوب يدويًا إذا كان *this مرتبطًا بالفعل بنفس الخادم مثل rhs (ربما يرسل رمز "إعادة التعيين" إذا كان مفيدًا) ، في حين أن منهج النسخ والمبادلة يستدعي النسخ من المرجح أن تكون مكتوبة لفتح اتصال مقبس متميز ثم إغلاق المستند الأصلي. لا يمكن أن يعني ذلك فقط تفاعل شبكة الاتصال عن بعد بدلاً من نسخة متغيرة بسيطة في العملية ، يمكن أن تتعارض مع حدود العميل أو الخادم على موارد أو اتصالات مأخذ التوصيل. (بالطبع هذا الفصل لديه واجهة مرعبة جدا ، ولكن هذه مسألة أخرى ؛- P).







c++ c bit-manipulation bitwise-operators