void *에 대한 합법적 인 사용이 있습니까?


87

void*C ++에서를 합법적으로 사용 합니까? 아니면 C가 있었기 때문에 도입 되었습니까?

내 생각을 요약하자면 :

입력 : 여러 입력 유형을 허용하려면 함수와 메서드를 오버로드 할 수 있습니다. 대신 공통 기본 클래스 또는 템플릿을 정의 할 수 있습니다 (답변에 언급 해 주셔서 감사합니다). 두 경우 모두 코드가 더 설명적이고 오류 발생 가능성이 적습니다 (기본 클래스가 정상적인 방식으로 구현 된 경우).

출력 : void*알려진 기본 클래스에서 파생 된 것보다 수신 하고 싶은 상황을 생각할 수 없습니다 .

내가 의미하는 바를 명확히하기 위해 :에 대한 유스 케이스가 있는지 구체적으로 묻는 것이 아니라 최선의 선택 또는 사용 가능한 선택 void*이있는 경우가 있는지 여부 를 구체적으로 묻지 않습니다 void*. 아래의 여러 사람들이 완벽하게 답변했습니다.


3
int 및 std :: string과 같은 여러 유형을 갖고 싶을 때 어떻습니까?
Amir

12
@Amir는 variant, any노조 태그. 실제 콘텐츠 유형을 알려주고 사용하기에 더 안전한 모든 것.
Revolver_Ocelot 2015

15
"C has it"은 충분히 강력한 정당성이며 더 이상 찾을 필요가 없습니다. 가능한 한 피하는 것은 두 언어 모두에서 좋은 것입니다.
n. '대명사'm.

1
한 가지 짚어 보자. C 스타일 API와의 상호 운용은 그것 없이는 어색하다.
Martin James

1
흥미로운 사용법은 포인터 벡터에 대한 유형 삭제 입니다
Paolo M

답변:


81

void*적어도 ::operator new(또한 모든 operator new...) 의 결과 및 malloc배치 new연산자 의 인수로 필요합니다 .

void*모든 포인터 유형의 공통 상위 유형으로 생각할 수 있습니다. 따라서 그것은 정확히에 대한 포인터를 의미하는 것이 아니라 void어떤 것에 대한 포인터를 의미합니다.

BTW, 관련되지 않은 여러 전역 변수에 대한 일부 데이터를 유지하려면 std::map<void*,int> score; global int x;and double y;and std::string s;do score[&x]=1;and score[&y]=2;and 를 선언 한 후 일부 를 사용할 수 있습니다.score[&z]=3;

memsetvoid*주소를 원함 (가장 일반적인 주소)

또한 POSIX 시스템에는 dlsym이 있으며 반환 유형은 분명히void*


1
그것은 아주 좋은 지적입니다. 구현 측면보다는 언어 사용자로부터 더 많이 생각하고 있다는 것을 언급하는 것을 잊었습니다. 내가 이해하지 못하는 한 가지 더 : 새로운 기능이 있습니까? 나는 더 많은 키워드로 생각
magu_

9
new는 + 및 sizeof와 같은 연산자입니다.
Joshua

7
물론 메모리 char *도를 통해 반환 될 수 있습니다 . 의미는 조금 다르지만이 측면에 매우 가깝습니다.
edmz

@ 조슈아. 몰랐습니다. 가르쳐 주셔서 감사합니다. C++이 글이 쓰여 있기 C++때문에 충분한 이유 void*입니다. 이 사이트를 좋아해야합니다. 한 가지 질문을 통해 많은 것을 배우십시오.
magu_

3
@black 예전에 C는 타입 도 없었 습니다 void*. 모든 표준 라이브러리 함수를 사용char*
콜 존슨에게

28

를 사용하는 데는 여러 가지 이유가 있으며 void*가장 일반적인 3 가지 이유는 다음과 같습니다.

  1. void*인터페이스에서 사용하여 C 라이브러리와 상호 작용
  2. 유형 삭제
  3. 형식화되지 않은 메모리 표시

역순 void*으로 char*(또는 변형) 대신 (3) 으로 형식화되지 않은 메모리를 표시 하면 우발적 인 포인터 산술을 방지하는 데 도움이됩니다. 사용 가능한 작업이 거의 void*없으므로 일반적으로 유용하기 전에 캐스팅이 필요합니다. 물론 char*앨리어싱에 문제가없는 것과 매우 비슷 합니다.

Type-erasure (2)는 템플릿과 함께 C ++에서 계속 사용됩니다.

  • 비 제네릭 코드는 바이너리 팽창을 줄이는 데 도움이되며 일반 코드에서도 콜드 경로에 유용합니다.
  • 비 제네릭 코드는 때때로 저장을 위해 필요합니다. std::function

그리고 당연히 당신이 다루는 인터페이스가 void*(1)을 사용할 때 당신은 선택의 여지가 거의 없습니다.


15

어 그래. C ++에서도 때때로 우리 void *template<class T*> 때로는 템플릿 확장에서 추가 코드가 너무 많은 무게 때문이다.

일반적으로 유형의 실제 구현으로 사용하고 템플릿 유형은 여기에서 상속하고 캐스트를 래핑합니다.

또한 사용자 지정 슬랩 할당 자 (운영자 새 구현)는 void *. 이것이 g ++ void *가 마치 크기가 1 인 것처럼 포인터 산술을 허용하는 확장을 추가 한 이유 중 하나입니다 .


답변 주셔서 감사합니다. 당신 말이 맞아요 내가 템플릿을 언급하는 걸 잊었 어. 하지만 성능 저하가 거의 발생하지 않기 때문에 템플릿이 작업에 더 적합하지 않을까요? ( stackoverflow.com/questions/2442358/… )
magu_

1
템플릿은 코드 크기 패널티를 도입하며 대부분의 경우 성능 패널티이기도합니다.
Joshua

struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;템플릿을 사용하는 최소 void-*-simulator입니다.
user253751 dec.

3
@Joshua : "대부분"에 대한 인용문이 필요합니다.
user541686

@Mehrdad : L1 캐시를 참조하십시오.
Joshua

10

입력 : 여러 입력 유형을 허용하려면 함수와 메서드를 오버로드 할 수 있습니다.

진실.

또는 공통 기본 클래스를 정의 할 수 있습니다.

이것은 부분적으로 사실입니다. 공통 기본 클래스, 인터페이스 또는 이와 유사한 것을 정의 할 없다면 어떻게 될까요? 이를 정의하려면 소스 코드에 대한 액세스 권한이 필요하며 이는 종종 불가능합니다.

템플릿에 대해서는 언급하지 않았습니다. 그러나 템플릿은 다형성에 도움이되지 않습니다. 즉, 컴파일 타임에 알려진 정적 유형에서 작동합니다.

void*가장 낮은 공통 분모로 간주 될 수 있습니다. C ++에서, 당신은 일반적으로 하지 않습니다 (i) 본질적으로 많은 것을 할 수없고 (ii) 거의 항상 더 나은 솔루션이 있기 때문에 .

또한 일반적으로 다른 구체적인 유형으로 변환하게됩니다. 그렇기 때문에 char *순수한 데이터 블록이 아닌 C 스타일 문자열을 기대하고 있음을 나타낼 수 있지만 일반적으로 더 좋습니다. 이것이 다른 포인터 유형에서 암시 적 캐스트를 허용하기 때문에 void*그보다 나은 이유 입니다 char*.

당신은 약간의 데이터를 받고, 그것으로 작업하고, 출력을 만들어 내야합니다. 이를 달성하려면 작업중인 데이터를 알아야합니다. 그렇지 않으면 원래 해결했던 문제가 아닌 다른 문제가 발생합니다. 많은 언어에는void* 에는 문제가없고 문제가 없습니다.

또 다른 합법적 인 사용

포인터와 같은 함수로 포인터 주소를 인쇄 할 때 유형 printf이 있어야 void*하므로 void* 로 캐스트해야 할 수 있습니다.


7

예, 언어의 다른 것만 큼 유용합니다.
예를 들어, 최소한의 유연한 인터페이스를 갖기 위해 필요할 때 올바른 유형으로 정적으로 캐스팅 할 수있는 클래스 유형을 지우는 데 사용할 수 있습니다.

에서 응답 당신에게 아이디어를 줄 것이다 사용의 예있다.
명확성을 위해 아래에 복사하여 붙여 넣습니다.

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;
};

분명히 이것은 void*.


4

포인터를 반환하는 외부 라이브러리 함수와 인터페이스. 다음은 Ada 애플리케이션 용입니다.

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

void* m_status_ptr = ada_function();

이것은 Ada가 당신에게 말하고 싶어했던 것에 대한 포인터를 반환합니다. 멋진 일을 할 필요가 없습니다. Ada에게 돌려 주면 다음 작업을 수행 할 수 있습니다. 사실 C ++에서 Ada 포인터를 분리하는 것은 사소한 일이 아닙니다.


auto이 경우 대신 사용할 수 없습니까? 유형이 컴파일 타임에 알려져 있다고 가정합니다.
magu_

아, 요즘 우리는 아마 할 수있을 것입니다. 이 코드는 몇 년 전에 Ada 래퍼를 만들었을 때의 것입니다.
RedSonja 2015

Ada 유형은 사악 할 수 있습니다. 그들은 스스로 만들고, 그것을 까다롭게 만드는 데 비뚤어진 즐거움을 가지고 있습니다. 나는 인터페이스를 변경할 수 없었고, 너무 쉬웠을 것이고, 그 무효 포인터에 숨겨진 불쾌한 것들을 반환했습니다. 으.
RedSonja 2015

2

요컨대 C ++는 엄격한 언어 ( malloc () 와 같은 C 유물을 고려하지 않음 ) 로서 가능한 모든 유형의 공통 부모가 없기 때문에 void *가 필요합니다. 예를 들어 ObjC와 달리 object .


malloc그리고 new둘 다 return void *, 그래서 C ++에 객체 클래스가 있더라도 필요합니다
Dmitry Grigoryev

malloc은 유물이지만 엄격한 언어에서 new 는 객체를 반환해야합니다. *
nredko dec.

그러면 정수 배열을 어떻게 할당합니까?
Dmitry Grigoryev

@DmitryGrigoryev가을 operator new()반환 void *하지만 new표현식은 반환 하지 않습니다
MM

아니에요 1. 확실히 내가보고 싶은 가상 모든 클래스 위의 기본 클래스 객체를 입력 포함, int다음 어떻게 다른, 그것은 비 가상 EBC의 경우 2 void*?
lorro

1

내 마음에 가장 먼저 발생하는 것은 (위의 몇 가지 대답의 구체적인 사례라고 생각하는) Windows의 threadproc에 개체 인스턴스를 전달할 수있는 기능입니다.

이 작업을 수행해야하는 두 개의 C ++ 클래스가 있습니다. 작업자 스레드 구현이 있고 CreateThread () API의 LPVOID 매개 변수가 클래스에서 정적 메서드 구현의 주소를 가져와 작업자 스레드가 작업을 수행 할 수 있습니다. 클래스의 특정 인스턴스. threadproc에서 단순 정적 캐스트 백은 인스턴스가 작동하도록 만들어 각 인스턴스화 된 객체가 단일 정적 메서드 구현의 작업자 스레드를 가질 수 있도록합니다.


0

다중 상속의 경우에는, 당신은 메모리 청크의 첫 번째 바이트에 대한 포인터를 얻을 필요가 있다면, 당신은 수있는 객체에 의해 점령 dynamic_castvoid*.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.