Geçersiz * için meşru bir kullanım var mı?


87

void*C ++ ' da yasal bir kullanım var mı ? Yoksa bu C'ye sahip olduğu için mi tanıtıldı?

Sadece düşüncelerimi özetlemek için:

Girdi : Birden fazla girdi türüne izin vermek istiyorsak, işlevleri ve yöntemleri aşırı yükleyebiliriz, alternatif olarak ortak bir temel sınıf veya şablon tanımlayabiliriz (yanıtlarda bundan bahsettiğiniz için teşekkürler). Her iki durumda da, kod daha açıklayıcı ve daha az hataya açık hale gelir (temel sınıfın mantıklı bir şekilde uygulanması koşuluyla).

Çıktı : void*Bilinen bir temel sınıftan türetilen bir şeyin aksine almayı tercih edeceğim herhangi bir durum düşünemiyorum .

Sadece ne demek istediğimi açıklığa kavuşturmak için: Özel olarak bir kullanım durumu olup olmadığını sormuyorum void*, ancak void*en iyi veya tek mevcut seçeneğin olduğu bir durum varsa . Aşağıda birkaç kişi tarafından mükemmel bir şekilde yanıtlanmıştır.


3
int & std :: string gibi birden çok türe sahip olmak istediğiniz zaman nasıl olur?
Amir

12
@Amir, variant, any, sendika etiketlendi. Size gerçek içerik türünü söyleyebilecek ve kullanımı daha güvenli olan her şey.
Revolver_Ocelot

15
"C has it" yeterince güçlü bir gerekçedir, daha fazlasını aramaya gerek yoktur. Bundan mümkün olduğunca kaçınmak her iki dilde de iyi bir şeydir.
n. zamirler 'm.

1
Ancak bir şey var: C tarzı API'ler ile birlikte çalışma, o olmadan gariptir.
Martin James

1
İlginç bir kullanım, işaretçilerin vektörleri için tür silme
Paolo M

Yanıtlar:


81

void*en azından ::operator new(her operator new...) sonucu mallocve yerleştirme newoperatörünün argümanı olarak gereklidir .

void*her işaretçi türünün ortak süper türü olarak düşünülebilir. Yani tam olarak işaretçi anlamına gelmez void, herhangi bir şeye işaret eder.

Birkaç ilgisiz küresel değişkenler için bazı verileri tutmak istiyorsa BTW, bazı kullanabilir std::map<void*,int> score; küresel ilan yaptıktan sonra, o int x;ve double y;ve std::string s;yapmak score[&x]=1;ve score[&y]=2;vescore[&z]=3;

memset bir void*adres istiyor (en genel olanlar)

Ayrıca, POSIX sistemlerinde dlsym vardır ve dönüş türü açıkçavoid*


1
Bu çok iyi bir nokta. Uygulama yönünden çok bir dil kullanıcısını düşündüğümü söylemeyi unuttum. Yine de anlamadığım bir şey daha var: Yeni bir işlev mi? Bunu daha çok bir anahtar kelime olarak düşündüm
magu_

9
new, + ve sizeof gibi bir operatördür.
Joshua

7
Elbette hafıza da bir üzerinden geri döndürülebilir char *. Anlam biraz farklı olsa da, bu yöne çok yakınlar.
edmz

@Joshua. Bunu bilmiyordum. Bana bunu öğrettiğin için teşekkür ederim. Yazıldığı C++için C++bu yeterli bir nedendir void*. Bu siteyi sevmelisin. Bir soru sor çok şey öğren.
magu_

3
Eskiden Geri @black, C hatta vermedi sahip bir void*tür. Kullanılan tüm standart kütüphane işlevlerichar*
Cole Johnson

28

Kullanmanın birçok nedeni vardır, void*en yaygın 3 tanesi:

  1. void*arayüzünde kullanarak bir C kütüphanesi ile etkileşim
  2. tür silme
  3. yazılmamış belleği ifade etmek

Ters sırada, yazılmamış belleği (veya varyantları) void*yerine (3) ile belirtmek, char*yanlışlıkla işaretçi aritmetiğini önlemeye yardımcı olur; üzerinde çok az işlem vardır, void*bu nedenle genellikle yararlı olmadan önce döküm gerektirir. Ve tabii ki, tıpkıchar* takma adla ilgili bir sorun olmadığı .

Tür silme (2), şablonlarla veya bağlantılı olarak C ++ 'da hala kullanılmaktadır:

  • genel olmayan kod, ikili şişkinliği azaltmaya yardımcı olur, genel kodda bile soğuk yollarda kullanışlıdır
  • genel olmayan kod bazen depolama için gereklidir, hatta genel bir kapsayıcıda bile std::function

Ve tabi ki, ele aldığınız arayüz void*(1) 'i kullandığında , çok az seçeneğiniz vardır.


15

Oh evet. C ++ 'da bile bazen void *yerinetemplate<class T*> şablon genişletmeden gelen fazladan kodun olmasından .

Yaygın olarak, bunu türün gerçek uygulaması olarak kullanırdım ve şablon türü ondan miras alır ve yayınları sarar.

Ayrıca, özel döşeme ayırıcıları (operatör yeni uygulamaları) kullanmalıdır void *. Bu, g ++ 'nın aritmatik göstericiye void *1 boyutundaymış gibi izin veren bir uzantı eklemesinin nedenlerinden biridir .


Cevabınız için teşekkür ederim. Haklısın, şablonlardan bahsetmeyi unuttum. Ancak, nadiren bir performans cezası getirdiği için şablonlar görev için daha uygun olmaz mıydı? ( Stackoverflow.com/questions/2442358/… )
magu_

1
Şablonlar, çoğu durumda performans cezası olan bir kod boyutu cezası getirir.
Joshua

struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;minimal bir boşluk - * - simülatörüdür.
user253751

3
@Joshua: "Çoğu" için bir alıntıya ihtiyacınız olacak.
user541686

@Mehrdad: L1 önbelleğine bakın.
Joshua

10

Girdi: Birden çok girdi türüne izin vermek istiyorsak, işlevleri ve yöntemleri aşırı yükleyebiliriz

Doğru.

alternatif olarak ortak bir temel sınıf tanımlayabiliriz.

Bu kısmen doğru: ya yapamazsan aynı temel sınıfa, bir arabirim veya benzeri tanımlar? Bunları tanımlamak için kaynak koduna erişmeniz gerekir ki bu genellikle mümkün değildir.

Şablonlardan bahsetmedin. Bununla birlikte, şablonlar polimorfizm konusunda size yardımcı olamaz: statik türlerle çalışırlar, yani derleme zamanında bilinenlerle çalışırlar.

void*en düşük ortak payda olarak kabul edilebilir. C ++ 'da, genellikle buna ihtiyacınız yoktur çünkü (i) doğası gereği onunla fazla bir şey yapamazsınız ve (ii) neredeyse her zaman daha iyi çözümler vardır.

Daha da ötesi, tipik olarak onu diğer beton türlerine dönüştürmeye başlayacaksınız. Bu yüzden char *genellikle daha iyidir, ancak saf bir veri bloğu yerine C tarzı bir dize beklediğinizi gösterebilir. Bu yüzden void*bundan daha iyidir char*, çünkü diğer işaretçi türlerinden örtük atamaya izin verir.

Bazı veriler almanız, onunla çalışmanız ve bir çıktı üretmeniz gerekiyor; bunu başarmak için, üzerinde çalıştığınız verileri bilmeniz gerekir, aksi takdirde başlangıçta çözdüğünüz olmayan farklı bir probleminiz olur. Birçok dilde yokvoid*Örneğin, sorunu yoktur ve hiçbir sorunu yoktur.

Başka bir meşru kullanım

İşaretçi gibi işlevlere sahip işaretçi adreslerini yazdırırken tipi printfolacaktır void*ve bu nedenle, void*


7

Evet, dildeki diğer şeyler kadar faydalıdır.
Örnek olarak, minimal ve esnek bir arayüze sahip olmak için gerektiğinde statik olarak doğru türe dönüştürebileceğiniz bir sınıfın türünü silmek için kullanabilirsiniz.

Gelen bu yanıtın size bir fikir verecektir kullanımına ilişkin bir örneği yoktur.
Anlaşılır olması için kopyalayıp aşağıya yapıştırıyorum:

class Dispatcher {
    Dispatcher() { }

    template<class C, void(C::*M)() = C::receive>
    static void invoke(void *instance) {
        (static_cast<C*>(instance)->*M)();
    }

public:
    template<class C, void(C::*M)() = &C::receive>
    static Dispatcher create(C *instance) {
        Dispatcher d;
        d.fn = &invoke<C, M>;
        d.instance = instance;
        return d;
    }

    void operator()() {
        (fn)(instance);
    }

private:
    using Fn = void(*)(void *);
    Fn fn;
    void *instance;
};

Açıkçası, bu, kullanım alanlarından yalnızca biridir void*.


4

Bir işaretçi döndüren harici bir kitaplık işleviyle etkileşim. İşte Ada uygulaması için bir tane.

extern "C" { void* ada_function();}

void* m_status_ptr = ada_function();

Bu, Ada'nın size anlatmak istediği şeye bir işaretçi döndürür. Onunla süslü bir şey yapmak zorunda değilsin, bir sonraki şeyi yapması için Ada'ya geri verebilirsin. Aslında bir Ada işaretçisini C ++ 'da çözmek önemsiz değildir.


autoBunun yerine bu durumda kullanamaz mıyız ? Türün derleme zamanında bilindiğini varsayarsak.
magu_

Ah, bugünlerde muhtemelen yapabiliriz. Bu kod, birkaç yıl önce, bazı Ada sarmalayıcıları yaptığım zamandan.
RedSonja

Ada tipleri şeytani olabilir - kendileri yaparlar, bilirsiniz ve onu zorlaştırmaktan sapık bir zevk alırlar. Arayüzü değiştirmeme izin verilmedi, bu çok kolay olurdu ve bu boşluk işaretçilerinde gizli bazı kötü şeyler döndürdü. Ugh.
RedSonja

2

Kısacası, katı bir dil olarak C ++ ( malloc () gibi C kalıntılarını hesaba katmadan ) void * gerektirir çünkü olası tüm türlerde ortak bir ebeveyni yoktur. Örneğin, nesnesi olan ObjC'nin aksine .


mallocve newher ikisi de geri döner void *, bu nedenle C ++ 'da bir nesne sınıfı olsa bile ihtiyacınız olacak
Dmitry Grigoryev

malloc
kalıntıdır

o zaman bir tamsayı dizisini nasıl ayırırsınız?
Dmitry Grigoryev

@DmitryGrigoryev operator new()geri dönüyor void *, ancak newifade dönmüyor
MM

1. Her sınıf ve türün üzerinde sanal bir temel sınıf nesnesi görmek istediğimden emin değilim , 2. eğer sanal olmayan EBC ise, o zaman bundan farkı nedir? intvoid*
lorro

1

Aklıma gelen ilk şey (yukarıdaki cevaplardan birkaçının somut bir örneğidir) Windows'ta bir nesne örneğini bir threadproc'a geçirme yeteneğidir.

Bunu yapması gereken birkaç C ++ sınıfım var, bunların işçi iş parçacığı uygulamaları var ve CreateThread () API'sindeki LPVOID parametresi, sınıfta statik bir yöntem uygulamasının adresini alıyor, böylece işçi iş parçacığı çalışabilir sınıfın belirli bir örneği. Threadproc'deki basit statik geri dönüşüm, örneklendirilen her nesnenin tek bir statik yöntem uygulamasından bir çalışan iş parçacığına sahip olmasına izin vererek çalışacağı örneği verir.


0

Çoklu miras durumda, bir bellek yığın ilk byte için bir işaretçi almak gerekiyorsa, sen olabilir bir nesne tarafından işgal dynamic_castetmek void*.

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.