Aşağıdaki koda sahip olduğumu varsayalım:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
Bu güvenli mi? Yoksa silme ptr
işleminden char*
önce atılması mı gerekiyor?
Aşağıdaki koda sahip olduğumu varsayalım:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
Bu güvenli mi? Yoksa silme ptr
işleminden char*
önce atılması mı gerekiyor?
Yanıtlar:
"Güvenli" e bağlıdır. Genellikle işe yarayacaktır çünkü bilgi, tahsisin kendisi hakkında gösterici ile birlikte depolanır, böylece ayırıcı onu doğru yere döndürebilir. Bu anlamda, ayırıcınız dahili sınır etiketleri kullandığı sürece "güvenlidir". (Çoğu yapar.)
Ancak, diğer yanıtlarda da belirtildiği gibi, bir geçersiz göstericinin silinmesi yıkıcıları çağırmayacaktır, bu da bir problem olabilir. Bu anlamda "güvenli" değil.
Yaptığın şeyi, yaptığın şekilde yapmak için iyi bir sebep yok. Kendi serbest bırakma işlevlerinizi yazmak istiyorsanız, doğru türde işlevler oluşturmak için işlev şablonlarını kullanabilirsiniz. Bunu yapmanın iyi bir nedeni, belirli türler için son derece verimli olabilen havuz ayırıcılar oluşturmaktır.
Diğer yanıtlarda da belirtildiği gibi, bu C ++ ' da tanımlanmamış bir davranıştır . Genel olarak, konunun kendisi karmaşık ve çelişkili fikirlerle dolu olmasına rağmen, tanımlanmamış davranışlardan kaçınmak iyidir.
sizeof(T*) == sizeof(U*)
tüm T,U
şablon bir 1 olmıyanları mümkün olmalıdır düşündürmektedir void *
tabanlı Çöp toplayıcı uygulaması. Ama sonra, gc aslında bir işaretçiyi silmek / serbest bırakmak zorunda kaldığında tam olarak bu soru ortaya çıkar. Çalışmasını sağlamak için ya lambda işlevi yıkıcı sarmalayıcılara (urgh) ihtiyacınız var ya da bir tür ile depolanabilir bir şey arasında gidip gelmeye izin veren bir tür dinamik "veri olarak tür" türünde bir şeye ihtiyacınız olacak.
Boş gösterici aracılığıyla silme, C ++ Standardı tarafından tanımlanmamıştır - bkz. Bölüm 5.3.5 / 3:
İlk alternatifte (nesneyi sil), işlenenin statik türü dinamik türünden farklıysa, statik tür, işlenenin dinamik türünün temel bir sınıfı olmalıdır ve statik tür, sanal bir yıkıcıya sahip olmalıdır veya davranış tanımsız . İkinci alternatifte (dizi sil), silinecek nesnenin dinamik türü statik türünden farklıysa, davranış tanımsızdır.
Ve dipnotu:
Bu, void * türünde bir işaretçi kullanılarak bir nesnenin silinemeyeceği anlamına gelir çünkü void türünde nesne yoktur
.
NULL
uygulama bellek yönetimi için herhangi bir fark yaratacak şekilde doldurun mu?
Bu iyi bir fikir değil ve C ++ 'da yapacağınız bir şey değil. Tür bilgilerinizi sebepsiz yere kaybediyorsunuz.
Yıkıcınız, dizinizdeki sildiğiniz nesnelere, onu ilkel olmayan türler için çağırdığınızda çağrılmaz.
Bunun yerine yeni / sil'i geçersiz kılmalısınız.
Boşluğu * silmek muhtemelen hafızanızı şans eseri doğru bir şekilde boşaltacaktır, ancak bu yanlıştır çünkü sonuçlar tanımsızdır.
Eğer bilmediğim bir sebepten dolayı işaretçinizi bir boşlukta * saklamanız gerekiyorsa, o zaman serbest bırakın, malloc ve bedava kullanmalısınız.
Soru hiçbir anlam ifade etmiyor. Kafanızın karışması kısmen, insanların sıklıkla kullandığı özensiz dilden kaynaklanıyor olabilir delete
:
Dinamik olarak tahsis edilmiş delete
bir nesneyi yok etmek için kullanırsınız . Bunu yapın, o nesneye bir işaretçi ile bir silme ifadesi oluşturursunuz . Asla "işaretçiyi silemezsiniz". Gerçekte yaptığınız şey "adresiyle tanımlanan bir nesneyi silmektir".
Şimdi sorunun neden anlamsız olduğunu görüyoruz: Boş işaretçi "bir nesnenin adresi" değildir. Bu, herhangi bir anlam bilgisi olmayan bir adres. Bu olabilir , gerçek bir nesnenin adresinden gelmiş, ancak kodlanmış çünkü bu bilgiler, kaybolur tip orijinal işaretçisi. Bir nesne işaretçisini geri yüklemenin tek yolu, boşluk işaretçisini bir nesne işaretçisine geri döndürmektir (bu, yazarın işaretçinin ne anlama geldiğini bilmesini gerektirir). void
kendisi eksik bir türdür ve bu nedenle asla bir nesnenin türü değildir ve bir nesneyi tanımlamak için asla bir boş gösterici kullanılamaz. (Nesneler, türleri ve adresleri ile birlikte tanımlanır.)
delete
bir boş işaretçi değeri, önceki bir yeni ifade tarafından oluşturulan dizi olmayan bir nesneye bir işaretçi veya bir Bu tür bir nesnenin temel sınıfını temsil eden alt nesne. Değilse, davranış tanımsızdır. " Yani bir derleyici kodunuzu teşhis olmadan kabul ederse, bu derleyicideki bir hatadan başka bir şey değildir ...
delete void_pointer
. Tanımlanmamış bir davranış. Yanıt programcının yapmak istediği şeyi yapıyor gibi görünse bile, programcılar hiçbir zaman tanımlanmamış davranışa başvurmamalıdır.
Bunu gerçekten yapmanız gerekiyorsa, neden aracı kişiyi ( new
ve delete
operatörleri) kesip küresel operator new
ve operator delete
doğrudan aramıyorsunuz ? (Elbette, new
ve delete
operatörlerini ayarlamaya çalışıyorsanız , aslında yeniden uygulamalısınız operator new
ve operator delete
.)
void* my_alloc (size_t size)
{
return ::operator new(size);
}
void my_free (void* ptr)
{
::operator delete(ptr);
}
Unutmayın, aksine malloc()
, başarısızlık üzerine operator new
fırlatır std::bad_alloc
(veya new_handler
eğer kayıtlıysa çağırır ).
Çünkü karakterin özel bir yıkıcı mantığı yoktur. BU işe yaramayacak.
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
D'ctor aranmayacak.
Void * kullanmak istiyorsanız, neden sadece malloc / free kullanmıyorsunuz? new / delete, bellek yönetiminden daha fazlasıdır. Temel olarak, new / delete bir kurucu / yıkıcı çağırır ve daha fazla şey olur. Yalnızca yerleşik türleri (char * gibi) kullanırsanız ve bunları void * aracılığıyla silerseniz, işe yarayacaktır, ancak yine de önerilmez. Sonuç olarak, void * kullanmak istiyorsanız malloc / free kullanın. Aksi takdirde, rahatınız için şablon işlevlerini kullanabilirsiniz.
template<typename T>
T* my_alloc (size_t size)
{
return new T [size];
}
template<typename T>
void my_free (T* ptr)
{
delete [] ptr;
}
int main(void)
{
char* pChar = my_alloc<char>(10);
my_free(pChar);
}
Bir çok insan zaten hayır, bir boşluk işaretçisini silmenin güvenli olmadığını söyleyerek yorum yaptı. Buna katılıyorum, ancak bitişik dizileri veya benzer bir şeyi tahsis etmek için boşluk işaretçileriyle çalışıyorsanız, bunu güvenle new
kullanabilmeniz için bunu yapabileceğinizi de eklemek istedim delete
(ahem ile , biraz fazla iş). Bu, bellek bölgesine ('arena' olarak adlandırılır) bir boşluk gösterici tahsis ederek ve ardından göstericiyi arenaya yeniye sunarak yapılır. C ++ SSS'de bu bölüme bakın . Bu, C ++ 'da bellek havuzlarını uygulamaya yönelik yaygın bir yaklaşımdır.
Bunu yapmak için neredeyse hiçbir neden yok.
Her şeyden önce, sen bilmiyorsan türünü verilerinin ve tanıdığınız tüm bu olmasıdır void*
o zaman gerçekten sadece bir typeless olarak bu verileri tedavi edilmelidir, damla ikili veri (bir unsigned char*
) ve kullanım malloc
/ free
başa . Bu bazen dalga formu verileri ve benzerleri gibi, void*
işaretçileri C apis'e iletmeniz gereken şeyler için gereklidir . Bu iyi.
Eğer varsa yapmak verinin türünü (yani bir ctor / dtor vardır) biliyorum, ama nedense bir ile sona erdi void*
(herhangi bir nedenle var) pointer o zaman gerçekten türüne geri kullanması gereklidir sen bunu biliyorsun ol ve delete
onu çağır .
Kod yansıtma ve diğer belirsizlik özellikleri için çerçevemde void *, (bilinmeyen türler) kullandım ve şu ana kadar herhangi bir derleyiciden herhangi bir sorun (bellek sızıntısı, erişim ihlalleri vb.) Yaşamadım. Yalnızca işlemin standart olmaması nedeniyle uyarılar.
Bilinmeyen bir (boşluğu *) silmek tamamen mantıklıdır. İmlecin bu yönergeleri izlediğinden emin olun, aksi takdirde anlam ifade etmeyebilir:
1) Bilinmeyen işaretçi, önemsiz bir yapısökücüsü olan bir türe işaret etmemelidir ve bu nedenle bilinmeyen bir işaretçi olarak kullanıldığında, ASLA SİLİNMEMELİDİR. Bilinmeyen işaretçiyi yalnızca ORİJİNAL türe geri çevirdikten SONRA silin.
2) Örneğe yığın bağlantılı veya yığın bağlantılı bellekte bilinmeyen bir işaretçi olarak mı başvuruluyor? Bilinmeyen işaretçi yığındaki bir örneğe başvuruyorsa, ASLA SİLİNMEMELİDİR!
3) Bilinmeyen işaretçinin geçerli bir bellek bölgesi olduğundan% 100 emin misiniz? Hayır, o zaman ASLA DELTEDİLMEMELİ!
Genel olarak, bilinmeyen (void *) bir işaretçi türü kullanılarak yapılabilecek çok az doğrudan iş vardır. Bununla birlikte, dolaylı olarak void *, C ++ geliştiricilerinin veri belirsizliği gerektiğinde güvenebilecekleri harika bir varlıktır.
Sadece bir arabellek istiyorsanız, malloc / free kullanın. New / delete kullanmanız gerekiyorsa, önemsiz bir sarmalayıcı sınıfını düşünün:
template<int size_ > struct size_buffer {
char data_[ size_];
operator void*() { return (void*)&data_; }
};
typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer
OpaqueBuffer* ptr = new OpaqueBuffer();
delete ptr;
Özel char durumu için.
char, özel bir yıkıcıya sahip olmayan içsel bir türdür. Yani sızıntı argümanları tartışmalı bir konudur.
sizeof (char) genellikle bir olduğundan, hizalama argümanı da yoktur. Sizeof (char) 'un bir olmadığı nadir platformlarda, karakterleri için yeterince hizalanmış bellek ayırırlar. Dolayısıyla hizalama argümanı da tartışmalı bir argümandır.
malloc / free bu durumda daha hızlı olacaktır. Ancak std :: bad_alloc'u kaybedersiniz ve malloc'un sonucunu kontrol etmeniz gerekir. Global yeni ve silme operatörlerini çağırmak, aracıyı atladığı için daha iyi olabilir.
new
atmak için tanımlandığını düşünmüyorlar . Bu doğru değil. Derleyici ve derleyici anahtarına bağlıdır. Örneğin MSVC2019 /GX[-] enable C++ EH (same as /EHsc)
anahtarlarına bakın . Ayrıca gömülü sistemlerde çoğu, C ++ istisnaları için performans vergilerini ödememeyi tercih eder. Yani "Ama std :: bad_alloc ..." ile başlayan cümle sorgulanabilir.