#Define, enum veya const kullanmalı mıyım?


125

Üzerinde çalıştığım bir C ++ projesinde, , dört değere sahip olabilen bayrak türü bir değere . Bu dört bayrak birleştirilebilir. Bayraklar, veritabanındaki kayıtları tanımlar ve şunlar olabilir:

  • Yeni Rekor
  • silinmiş kayıt
  • değiştirilmiş kayıt
  • mevcut kayıt

Şimdi, her kayıt için bu özniteliği korumak istiyorum, böylece bir enum kullanabileyim:

enum { xNew, xDeleted, xModified, xExisting }

Bununla birlikte, kodun diğer yerlerinde, hangi kayıtların kullanıcı tarafından görülebileceğini seçmem gerekiyor, bu yüzden bunu tek bir parametre olarak iletmek istiyorum, örneğin:

showRecords(xNew | xDeleted);

Görünüşe göre üç olası yaklaşımım var:

#define X_NEW      0x01
#define X_DELETED  0x02
#define X_MODIFIED 0x04
#define X_EXISTING 0x08

veya

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

veya

namespace RecordType {
    static const uint8 xNew = 1;
    static const uint8 xDeleted = 2;
    static const uint8 xModified = 4;
    static const uint8 xExisting = 8;
}

Alan gereksinimleri önemlidir (bayt vs int) ancak çok önemli değildir. Tanımlarla, tip güvenliğini kaybediyorum veenum biraz boşluk (tamsayı) kaybederim ve muhtemelen bitsel bir işlem yapmak istediğimde çevrim yapmam gerekir. İle constben rastgele beri I da kaybetmek tip güvenliğini düşünmekuint8 yanlışlıkla alabilir.

Daha temiz bir yol var mı?

Değilse, ne kullanırsınız ve neden?

Not: Kodun geri kalanı, #defines içermeyen oldukça temiz, modern C ++ ve birkaç alanda ad alanları ve şablonlar kullandım, bu yüzden bunlar da söz konusu değil.


"enum ile biraz boşluk kaybederim (tam sayılar)". Şart değil. Stackoverflow.com/questions/366017/… ve stackoverflow.com/questions/1113855/… sayfalarına bakın (ve gcc's -fshort-enum . (Bu C yanıtlarının C ++ için hala doğru olduğunu varsayıyorum.)
idbrii

@pydave C ve C ++ uyumluluğundan emin değilseniz, bu bağlantıyı çok yararlı buluyorum, örneğin enum david.tribble.com/text/cdiffs.htm#C99-enum-type
aka.nice

3
Bu, yüksek oy alan eski bir konudur, bu sorun durumu için C ++ 11 enum sınıflarından bahsetmemek için bir neden var mı?
Brandin

Not olarak, enum RecordType : uint8_ttür güvenliğini enumküçük boyutla birleştirir uint8_t, ancak yine de bit düzeyinde operatörler sağlamanız gerekir.
Justin Time -

Yanıtlar:


88

Tek bir yaklaşımın dezavantajlarını azaltmak için stratejileri birleştirin. Gömülü sistemlerde çalışıyorum, bu nedenle aşağıdaki çözüm tamsayı ve bitsel operatörlerin hızlı, düşük bellek ve flash kullanımının düşük olduğu gerçeğine dayanıyor.

Sabitlerin genel ad alanını kirletmesini önlemek için numaralandırmayı bir ad alanına yerleştirin.

namespace RecordType {

Bir enum, yazılan kontrol edilen bir derleme zamanını bildirir ve tanımlar. Bağımsız değişkenlere ve değişkenlere doğru tür verildiğinden emin olmak için her zaman derleme zamanı türü denetimini kullanın. C ++ 'da typedef'e gerek yoktur.

enum TRecordType { xNew = 1, xDeleted = 2, xModified = 4, xExisting = 8,

Geçersiz bir durum için başka bir üye oluşturun. Bu, hata kodu olarak yararlı olabilir; örneğin, durumu geri döndürmek istediğinizde ancak G / Ç işlemi başarısız olduğunda. Ayrıca hata ayıklama için de kullanışlıdır; değişkenin değerinin kullanılması gerekip gerekmediğini bilmek için onu başlatma listelerinde ve yıkıcılarda kullanın.

xInvalid = 16 };

Bu tür için iki amacınız olduğunu düşünün. Bir kaydın mevcut durumunu izlemek ve belirli durumlarda kayıtları seçmek için bir maske oluşturmak için. Türün değerinin amacınız için geçerli olup olmadığını test etmek için bir satır içi işlev oluşturun; bir durum işaretine karşı bir durum maskesi olarak. Bu typedef, yalnızca bir intve bir değer 0xDEADBEEFolduğu için, değişkeninizdeki gibi, başlangıçlanmamış veya yanlış yönlendirilmiş değişkenler aracılığıyla olabilecek hataları yakalayacaktır .

inline bool IsValidState( TRecordType v) {
    switch(v) { case xNew: case xDeleted: case xModified: case xExisting: return true; }
    return false;
}

 inline bool IsValidMask( TRecordType v) {
    return v >= xNew  && v < xInvalid ;
}

usingTürü sık sık kullanmak istiyorsanız bir yönerge ekleyin .

using RecordType ::TRecordType ;

Değer denetleme işlevleri, kötü değerleri kullanıldıkları anda yakalama konusunda kullanışlıdır. Koşarken ne kadar çabuk hata yakalarsanız, o kadar az zarar verebilir.

İşte hepsini bir araya getirmek için bazı örnekler.

void showRecords(TRecordType mask) {
    assert(RecordType::IsValidMask(mask));
    // do stuff;
}

void wombleRecord(TRecord rec, TRecordType state) {
    assert(RecordType::IsValidState(state));
    if (RecordType ::xNew) {
    // ...
} in runtime

TRecordType updateRecord(TRecord rec, TRecordType newstate) {
    assert(RecordType::IsValidState(newstate));
    //...
    if (! access_was_successful) return RecordType ::xInvalid;
    return newstate;
}

Doğru değer güvenliğini sağlamanın tek yolu, operatör aşırı yüklemelerine sahip özel bir sınıf kullanmaktır ve bu, başka bir okuyucu için bir alıştırma olarak bırakılmıştır.


1
Çoğunlukla güzel bir cevap - ancak soru bayrakların birleştirilebileceğini ve IsValidState () işlevinin bunların birleştirilmesine izin vermediğini belirtiyor.
Jonathan Leffler

3
@Jonathan Leffler: Durduğum yerden 'IsValidState'in bunu yapmaması gerektiğini düşünüyorum,' IsValidMask 'öyle.
João Portela

1
IsValidMaskHiçbirini seçmeye izin vermemesi isteniyor 0mu (yani )?
Joachim Sauer

2
−1 Çalışma zamanı tip kontrolü fikri bir iğrençtir.
Şerefe ve hth. - Alf

54

Tanımları unutun

Kodunuzu kirletecekler.

bitfields?

struct RecordFlag {
    unsigned isnew:1, isdeleted:1, ismodified:1, isexisting:1;
};

Bunu asla kullanma . 4 inçten tasarruf etmekten çok hız ile ilgileniyorsunuz. Bit alanlarını kullanmak aslında diğer türlere erişimden daha yavaştır.

Bununla birlikte, yapılardaki bit üyelerinin pratik dezavantajları vardır. İlk olarak, bellekteki bitlerin sıralaması derleyiciden derleyiciye değişir. Ek olarak, birçok popüler derleyici , bit üyelerini okumak ve yazmak için verimsiz kod üretir ve potansiyel olarak ciddi iş parçacığı güvenliği sorunları vardır. çoğu makinenin bellekteki keyfi bit kümelerini işleyememesi nedeniyle bit alanlarıyla ilgili (özellikle çok işlemcili sistemlerde) ancak bunun yerine tam kelimeleri yüklemeli ve saklamalıdır. Örneğin, bir muteks kullanılmasına rağmen aşağıdakiler iş parçacığı açısından güvenli olmayacaktır

Kaynak: http://en.wikipedia.org/wiki/Bit_field :

Eğer için daha fazla neden ihtiyacın varsa değil bitfields kullanmak belki Raymond Chen onun sizi ikna edecek The Old New Thing mesaj: bitfields maliyet-fayda analizini boole koleksiyonu için de http://blogs.msdn.com/oldnewthing/ arşiv / 2008/11/26 / 9143050.aspx

const int?

namespace RecordType {
    static const uint8 xNew = 1;
    static const uint8 xDeleted = 2;
    static const uint8 xModified = 4;
    static const uint8 xExisting = 8;
}

Bunları bir ad alanına koymak harika. CPP veya başlık dosyanızda beyan edildiyse, değerleri satır içi olacaktır. Bu değerler üzerinde anahtarı kullanabileceksiniz, ancak bu, kuplajı biraz artıracaktır.

Ah, evet: statik anahtar kelimeyi kaldırın . static, sizin yaptığınız gibi kullanıldığında C ++ 'da kullanımdan kaldırılmıştır ve uint8 bir yapı tipiyse, bunu aynı modülün birden çok kaynağı tarafından dahil edilen bir başlıkta bildirmek için buna ihtiyacınız olmayacaktır. Sonunda, kod şöyle olmalıdır:

namespace RecordType {
    const uint8 xNew = 1;
    const uint8 xDeleted = 2;
    const uint8 xModified = 4;
    const uint8 xExisting = 8;
}

Bu yaklaşımın sorunu, kodunuzun sabitlerinizin değerini bilmesidir, bu da kuplajı biraz artırır.

Sıralama

Sabit int ile aynı, biraz daha güçlü bir yazımla.

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

Yine de küresel ad alanını kirletiyorlar. Bu arada ... typedef'i kaldırın . C ++ ile çalışıyorsunuz. Bu numaralandırma ve yapı türleri, kodu her şeyden daha fazla kirletiyor.

Sonuç biraz:

enum RecordType { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } ;

void doSomething(RecordType p_eMyEnum)
{
   if(p_eMyEnum == xNew)
   {
       // etc.
   }
}

Gördüğünüz gibi, numaranız genel ad alanını kirletiyor. Bu numaralandırmayı bir ad alanına koyarsanız, şöyle bir şeye sahip olursunuz:

namespace RecordType {
   enum Value { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } ;
}

void doSomething(RecordType::Value p_eMyEnum)
{
   if(p_eMyEnum == RecordType::xNew)
   {
       // etc.
   }
}

extern const int?

Kuplajı azaltmak istiyorsanız (yani sabitlerin değerlerini gizleyebilmek ve böylece tam bir yeniden derlemeye gerek kalmadan bunları istediğiniz gibi değiştirebilmek), int'leri başlıkta extern ve CPP dosyasında sabit olarak ilan edebilirsiniz. aşağıdaki örnekte olduğu gibi:

// Header.hpp
namespace RecordType {
    extern const uint8 xNew ;
    extern const uint8 xDeleted ;
    extern const uint8 xModified ;
    extern const uint8 xExisting ;
}

Ve:

// Source.hpp
namespace RecordType {
    const uint8 xNew = 1;
    const uint8 xDeleted = 2;
    const uint8 xModified = 4;
    const uint8 xExisting = 8;
}

Yine de bu sabitler üzerinde switch'i kullanamazsınız. Sonunda, zehirini seç ... :-p


5
Bit alanlarının neden yavaş olduğunu düşünüyorsunuz? Onu ve başka bir yöntemi kullanarak kodun profilini gerçekten çıkardınız mı? Öyle olsa bile, netlik hızdan daha önemli olabilir ve "bunu asla kullanma" biraz basitleştirilebilir.
wnoise

"statik sabit8 xNew;" yalnızca gereksizdir çünkü C ++ const ad alanı kapsamlı değişkenler varsayılan olarak dahili bağlantıya ayarlanır. "Const" ı kaldırın ve harici bağlantıya sahip. Ayrıca, "enum {...} RecordType;" türü anonim bir enum olan "RecordType" adlı genel bir değişken bildirir.
bk1e

onebyone: Birincisi, asıl neden kazancın (varsa birkaç bayt) kayıp tarafından gölgede bırakılmasıydı (erişim daha yavaş, hem okuma hem de yazma) ...
paercebal

3
onebyone: İkincisi, işte veya evde ürettiğim tüm kodlar doğası gereği iş parçacığı açısından güvenlidir. Yapması kolay: Küresel yok, statik yok, kilit korumalı olmadığı sürece iş parçacıkları arasında paylaşılmıyor. Bu deyimi kullanmak, bu temel iş parçacığı güvenliğini bozacaktır. Ve ne için? Belki birkaç bayt ? ... :-) ...
paercebal

Raymond Chen'in bitfields'ın gizli maliyetleri hakkındaki makalesine referans eklendi.
paercebal

30

Std :: bitset'i eklediniz mi? Bunun için bayrak setleri. Yapmak

typedef std::bitset<4> RecordType;

sonra

static const RecordType xNew(1);
static const RecordType xDeleted(2);
static const RecordType xModified(4);
static const RecordType xExisting(8);

Bit kümesi için çok sayıda operatör aşırı yüklemesi olduğundan, artık

RecordType rt = whatever;      // unsigned long or RecordType expression
rt |= xNew;                    // set 
rt &= ~xDeleted;               // clear 
if ((rt & xModified) != 0) ... // test

Veya buna çok benzer bir şey - bunu test etmediğim için herhangi bir düzeltmeyi takdir ediyorum. Bitlere dizine göre de başvurabilirsiniz, ancak genellikle tek bir sabit kümesi tanımlamak en iyisidir ve RecordType sabitleri muhtemelen daha kullanışlıdır.

Eğer Bit kümesiyle dışladı varsayarsak, ben oy enum .

Numaralandırmanın ciddi bir dezavantaj olduğunu kabul etmiyorum - Tamam, bu yüzden biraz gürültülü ve bir numaralandırmaya aralık dışı bir değer atamak tanımsız bir davranış, bu nedenle teorik olarak alışılmadık bir C ++ üzerinde kendinizi ayağa kaldırmak mümkün. uygulamalar. Ancak bunu yalnızca gerektiğinde yaparsanız (ki bu, int'ten enum iirc'ye geçerken), insanların daha önce gördüğü tamamen normal bir koddur.

Numaralamanın herhangi bir alan maliyeti konusunda da şüpheliyim. uint8 değişkenleri ve parametreleri muhtemelen ints'ten daha az yığın kullanmayacaktır, bu nedenle yalnızca sınıflarda depolama önemlidir. Bir yapıda birden fazla baytı paketlemenin kazanacağı bazı durumlar vardır (bu durumda numaralandırmaları uint8 depolamasının içine ve dışına çevirebilirsiniz), ancak normalde dolgu her şekilde faydayı kesecektir.

Dolayısıyla, numaralamanın diğerlerine kıyasla hiçbir dezavantajı yoktur ve bir avantaj olarak size biraz tür güvenliği sağlar (açıkça çevrim yapmadan rastgele bir tam sayı değeri atayamazsınız) ve her şeye atıfta bulunmanın temiz yolları.

Bu arada, tercih için "= 2" yi de numaralandırmaya koyardım. Bu gerekli değil, ancak "en az şaşkınlık ilkesi" 4 tanımın da aynı görünmesi gerektiğini gösteriyor.


1
Aslında bit setini hiç düşünmedim. Ancak bunun iyi olacağından emin değilim. Bit kümesiyle bitleri 1, 2, 3, 4 olarak ele almalıyım ki bu da kodu daha az okunabilir hale getirir - yani bitleri 'adlandırmak' için muhtemelen bir enum kullanacağım anlamına gelir. Yine de yer tasarrufu sağlayabilir. Teşekkürler.
Milan Babuškov

Milan, bitleri bir numaralandırma kullanarak "isimlendirmek" zorunda değilsiniz, sadece önceden tanımlanmış bitleri yukarıda gösterildiği gibi kullanabilirsiniz. My_bitset.flip (1) yerine bit bir'i açmak isterseniz, my_bitset | = xNew;
moswald

bu size daha az ve daha çok STL'ye yöneliktir, ancak: gerçekten sormak zorundayım: neden bunun bitsetiçin kullanasınız? genellikle longher eleman için bir (benim uygulamamda iirc; evet, ne kadar savurgan) veya benzer bir integral tipine çevrilir, öyleyse neden sadece gizlenmemiş integraller kullanmıyoruz? (veya bugünlerde constexprsıfır depolama ile)
altçizgi_d

[düzenleme zaman aşımı] ... ama sonra bitsetsınıfın mantığını gerçekten anlamadım, 'ugh'u çevreleyen tartışmalarda yinelenen bir alt akım gibi görünen şey dışında, dilin çirkin alt düzey köklerini örtmeliyiz '
underscore_d

" uint8değişkenler ve parametreler muhtemelen olduğundan daha az yığın kullanmaz ints" yanlıştır. 8-Bit kayıtlara sahip bir CPU'nuz varsa, sadece 1'e ihtiyaç intduyarken en az 2 kayıt uint8_tgerekir, bu nedenle daha fazla yığın alanına ihtiyacınız olacaktır çünkü yazmaçların dışında kalma olasılığınız daha yüksektir (bu da daha yavaştır ve kod boyutunu artırabilir) talimat setine bağlı olarak)). (Bir tip, olması gerektiği uint8_tdeğil uint8)
12431234123412341234123


5

Mümkünse makro KULLANMAYIN. Modern C ++ söz konusu olduğunda pek beğenilmiyorlar.


4
Doğru. Makrolar hakkında nefret ettiğim şey, yanılıyorlarsa onlara adım atamayacağınızdır.
Carl

Bunun derleyicide düzeltilebilecek bir şey olduğunu düşünüyorum.
celticminstrel

4

Numaralandırmalar, tür güvenliği kadar "tanımlayıcılara anlam" sağladıkları için daha uygun olacaktır. "XDeleted" in "RecordType" olduğunu ve bunun "kayıt türünü" temsil ettiğini (vay!) Yıllar sonra bile açıkça anlayabilirsiniz. Consts, bunun için yorum gerektirir, ayrıca kodda yukarı ve aşağı gitmeyi de gerektirir.


4

Tanımlarla tip güvenliğini kaybediyorum

Şart değil...

// signed defines
#define X_NEW      0x01u
#define X_NEW      (unsigned(0x01))  // if you find this more readable...

ve enum ile biraz alan kaybediyorum (tamsayılar)

Mutlaka değil - ancak depolama noktalarında açık olmanız gerekir ...

struct X
{
    RecordType recordType : 4;  // use exactly 4 bits...
    RecordType recordType2 : 4;  // use another 4 bits, typically in the same byte
    // of course, the overall record size may still be padded...
};

ve muhtemelen bitsel işlem yapmak istediğimde yayınlamam gerekiyor.

Acıdan kurtulmak için operatörler oluşturabilirsiniz:

RecordType operator|(RecordType lhs, RecordType rhs)
{
    return RecordType((unsigned)lhs | (unsigned)rhs);
}

Const ile rastgele bir uint8 yanlışlıkla içeri girebileceğinden, tür güvenliğini de kaybettim.

Aynısı bu mekanizmalardan herhangi birinde de olabilir: aralık ve değer kontrolleri normalde güvenlik tipine diktir (kullanıcı tanımlı türler - yani kendi sınıflarınız - verileri hakkında "değişmezler" uygulayabilir). Numaralandırmalarla, derleyici değerleri barındırmak için daha büyük bir tür seçmekte özgürdür ve başlatılmamış, bozuk veya sadece yanlış ayarlanmış bir enum değişkeni, bit modelini beklemediğiniz bir sayı olarak yorumlayabilir - eşit olmayan ile karşılaştırarak numaralandırma tanımlayıcıları, bunların herhangi bir kombinasyonu ve 0.

Daha temiz bir yol var mı? / Değilse, ne kullanırsınız ve neden?

Sonunda, denenmiş ve güvenilen C-tarzı bit tabanlı OR numaralandırmaları, resimdeki bit alanlarına ve özel operatörlere sahip olduğunuzda oldukça iyi çalışır. Mat_geek'in cevabında olduğu gibi bazı özel doğrulama işlevleri ve iddiaları ile sağlamlığınızı daha da artırabilirsiniz; string, int, double değerleri vb. işlemeye genellikle eşit derecede uygulanabilen teknikler.

Bunun "daha temiz" olduğunu iddia edebilirsiniz:

enum RecordType { New, Deleted, Modified, Existing };

showRecords([](RecordType r) { return r == New || r == Deleted; });

Kayıtsızım: veri bitleri daha sıkı paketleniyor ancak kod önemli ölçüde büyüyor ... kaç tane nesneye sahip olduğunuza bağlı ve lamdbas - onlar ne kadar güzel - bitsel OR'lardan daha karmaşık ve doğru elde etmek hala daha zor.

BTW / - iş parçacığı güvenliğinin oldukça zayıf IMHO'su hakkındaki argüman - baskın bir karar verme gücü olmaktan çok arka planda bir düşünce olarak en iyi hatırlanır; Bit alanları arasında bir muteks paylaşmak, paketlenmelerinin farkında olmasa bile daha olası bir uygulamadır (muteksler nispeten hantal veri üyeleridir - bir nesnenin üyeleri üzerinde birden fazla muteks bulundurmayı düşünmek için performans konusunda gerçekten endişelenmeliyim ve dikkatlice bakardım bit alanları olduklarını fark etmek için yeterli). Herhangi bir alt kelime boyutu türü aynı soruna sahip olabilir (örneğin a uint8_t). Her neyse, daha yüksek eşzamanlılık için umutsuzsanız, atomik karşılaştırma ve takas tarzı işlemleri deneyebilirsiniz.


1
+1 İyi. Ancak, talimattan önce operator|bir tamsayı türüne ( unsigned int) dönüştürülmelidir |. Aksi takdirde operator|, kendini yinelemeli olarak çağırır ve çalışma zamanı yığın taşmasına neden olur. Ben önermek: return RecordType( unsigned(lhs) | unsigned(rhs) );. Şerefe
olibre

3

Bir numaralandırmayı depolamak için 4 bayt kullanmanız gerekse bile (C ++ 'ya aşina değilim - C #' da temel alınan türü belirtebileceğinizi biliyorum), yine de buna değer - numaralandırma kullanın.

Günümüzde ve GB belleğe sahip sunucular çağında, genel olarak uygulama düzeyinde 4 bayt ve 1 bayt bellek gibi şeyler önemli değil. Elbette, sizin özel durumunuzda bellek kullanımı bu kadar önemliyse (ve numaralandırmayı desteklemek için C ++ 'nın bayt kullanmasını sağlayamıyorsanız), o zaman' statik sabit 'yolunu düşünebilirsiniz.

Günün sonunda, kendinize sormalısınız, veri yapınız için 3 bayt bellek tasarrufu için 'statik sabit' kullanmanın bakım darbesine değer mi?

Akılda tutulması gereken başka bir şey - IIRC, x86'da veri yapıları 4 bayt hizalıdır, bu nedenle 'kayıt' yapınızda çok sayıda bayt genişlikli öğeniz yoksa, aslında önemli olmayabilir. Performans / alan için sürdürülebilirlikte bir değiş tokuş yapmadan önce test edin ve yaptığından emin olun.


Dil revizyonu C ++ 11'den itibaren C ++ 'da temel alınan türü belirtebilirsiniz. O zamana kadar, "en azından belirtilen tüm numaralandırıcılar için depolamak ve bir bit alanı olarak kullanmak için yeterince büyük, ancak muhtemelen intçok küçük değilse" olduğuna inanıyorum. [C ++ 11'de temel alınan türü belirtmezseniz, eski davranışı kullanır. Tersine, C ++ 11'in enum classtemelini oluşturan tür, intaksi belirtilmedikçe açıkça varsayılan olarak
Justin Time - Monica'yı

3

Numaralandırma sözdizimi ve bit kontrolünün rahatlığıyla birlikte sınıfların tür güvenliğini istiyorsanız, C ++ 'daki Güvenli Etiketleri düşünün . Yazarla çalıştım ve oldukça zeki.

Yine de dikkatli olun. Sonunda, bu paket şablonlar ve makrolar kullanır !


Küçük uygulamam için aşırıya kaçmış gibi görünüyor. ama iyi bir çözüm gibi görünüyor.
Milan Babuškov

2

Aslında, kavramsal bir bütün olarak bayrak değerlerini geçirmeniz mi gerekiyor yoksa bayrak başına çok sayıda kodunuz olacak mı? Her iki durumda da, bunu 1 bitlik bit alanlarının sınıfı veya yapısı olarak almanın aslında daha net olabileceğini düşünüyorum:

struct RecordFlag {
    unsigned isnew:1, isdeleted:1, ismodified:1, isexisting:1;
};

Daha sonra kayıt sınıfınız bir struct RecordFlag üye değişkenine sahip olabilir, işlevler struct RecordFlag türünde argümanlar alabilir, vb. Derleyici bit alanlarını bir arada paketleyerek yerden tasarruf etmelidir.


Bazen bir bütün olarak, bazen bayrak olarak. Ayrıca, belirli bir bayrağın ayarlanıp ayarlanmadığını (bir bütün olarak geçtiğimde) test etmem gerekiyor.
Milan Babuškov

iyi, ayrıldığında, int isteyin. Birlikte olduğunuzda yapıyı geçin.
wnoise

Daha iyi olmayacak. Bit alanlarına erişim her şeyden daha yavaştır.
paercebal

Gerçekten mi? Derleyicinin bit alanlarını test etmek için manuel bit çevirmeden önemli ölçüde farklı kodlar üreteceğini mi düşünüyorsunuz? Ve önemli ölçüde daha yavaş olacak mı? Neden? Deyimsel olarak bu kadar kolay yapamayacağınız tek şey, aynı anda birden fazla bayrağı maskelemek.
wnoise

Basit bir okuma testi çalıştırarak, bit maskeleme için 5,50-5,58 saniye, bit alanı erişimi için 5,45-5,59 saniye elde ediyorum. Neredeyse ayırt edilemez.
wnoise

2

Değerlerin bir araya getirilebildiği bu tür bir şey için muhtemelen bir sıralama kullanmazdım, daha tipik olarak numaralandırmalar birbirini dışlayan durumlar olur.

Ancak, hangi yöntemi kullanırsanız kullanın, bunların birlikte birleştirilebilen bitler olduğunu daha net hale getirmek için, bunun yerine gerçek değerler için bu sözdizimini kullanın:

#define X_NEW      (1 << 0)
#define X_DELETED  (1 << 1)
#define X_MODIFIED (1 << 2)
#define X_EXISTING (1 << 3)

Burada bir sola kaydırma kullanmak, her bir değerin tek bir bit olması amaçlandığını belirtmeye yardımcı olur, daha sonra birinin yeni bir değer eklemek ve ona 9 değeri atamak gibi yanlış bir şey yapması daha az olasıdır.


1
Bunun için yeterince emsal vardır, özellikle ioctl () sabitlerinde. Yine de onaltılık sabitleri kullanmayı tercih ederim: 0x01, 0x02, 0x04, 0x08, 0x10, ...
Jonathan Leffler

2

Dayalı KISS , yüksek uyum ve düşük bağlantıya şu soruları sorun -

  • Kimin bilmesi gerekiyor? sınıfım, kitaplığım, diğer sınıflar, diğer kitaplıklar, 3. taraflar
  • Hangi düzeyde soyutlama sağlamam gerekiyor? Tüketici bit işlemlerini anlıyor mu?
  • VB / C # vb.'den arayüz yapmam gerekecek mi?

" Büyük Ölçekli C ++ Yazılım Tasarımı " adlı harika bir kitap var , bu temel türleri dışarıdan tanıtıyor, eğer başka bir başlık dosyası / arayüz bağımlılığından kaçınabiliyorsanız denemelisiniz.


1
a) 5-6 sınıf. b) sadece ben, bu tek kişilik bir proje c) arayüz yok
Milan Babuškov


0

İle gitmeyi tercih ederim

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

Nedeni:

  1. Daha temizdir ve kodu okunabilir ve bakımı yapılabilir hale getirir.
  2. Sabitleri mantıksal olarak gruplandırır.
  3. İşiniz sürece Programcı zaman daha önemlidir olduğu o 3 bayt kaydedin.

Pekala, kolayca bir milyon Record sınıfına sahip olabilirim, bu yüzden önemli olabilir. OTOH, bu sadece 1MB ile 4MB arasında bir fark, bu yüzden belki endişelenmemeliyim.
Milan Babuškov

@Vivek: Tamsayı genişliği sınırlamasını düşündünüz mü? Özellikle C ++ 11'den önce.
user2672165

0

Her şeyi aşırı mühendislik yapmayı sevdiğimden değil, ancak bazen bu durumlarda bu bilgiyi kapsayacak (küçük) bir sınıf oluşturmaya değer olabilir. Bir sınıf RecordType oluşturursanız, aşağıdaki gibi işlevlere sahip olabilir:

void setDeleted ();

void clearDeleted ();

bool isDeleted ();

vb ... (veya hangi kongre uygunsa)

Kombinasyonları doğrulayabilir (tüm kombinasyonların yasal olmadığı durumda, örneğin "yeni" ve "silinmiş" her ikisi aynı anda ayarlanamazsa). Bit maskeleri vb. Kullandıysanız, durumu belirleyen kodun doğrulanması gerekir, bir sınıf da bu mantığı kapsayabilir.

Sınıf ayrıca size her duruma anlamlı günlük kaydı bilgisi ekleme yeteneği verebilir, mevcut durumun bir dize gösterimini döndürmek için bir işlev ekleyebilirsiniz (veya akış operatörlerini '<<') kullanabilirsiniz.

Tüm bunlara rağmen, depolama konusunda endişeleriniz varsa, sınıfın yalnızca bir 'char' veri üyesine sahip olabilirsiniz, bu nedenle yalnızca küçük bir miktarda depolama alanı alın (sanal olmadığını varsayarak). Elbette donanıma vb. Bağlı olarak hizalama sorunları yaşayabilirsiniz.

Başlık dosyası yerine cpp dosyası içinde anonim bir ad alanı içindeyse, gerçek bit değerlerinin 'dünyanın' geri kalanı tarafından görülmemesine sahip olabilirsiniz.

Enum / # define / bitmask vb. Kullanan kodun geçersiz kombinasyonlar, günlük kaydı vb. İle başa çıkmak için çok sayıda "destek" kodu içerdiğini fark ederseniz, o zaman bir sınıftaki kapsülleme dikkate değer olabilir. Elbette çoğu zaman basit problemler basit çözümlerle daha iyidir ...


Maalesef, bildirimin proje genelinde kullanıldığı için (bazı 5-6 sınıflar tarafından kullanıldığından) bir .h dosyasında olması gerekir.
Milan Babuškov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.