2013-03-08 2 views
0

인터페이스의 사용자의 구현 세부 정보를 숨기려면 내가 다음과 같은 개념으로 생각 플릿 기능의 대폭적인 사용을 방지하기 위해 :캐스트

// data.h

#ifndef DATA_H_ 
#define DATA_H_ 

#include <cstddef> 

template <size_t N = 0> 
class Data 
{ 
    public: 
     const size_t n; 
     size_t values[N]; 
     Data<N>(); 
}; 

#endif // DATA_H_ 

// data.cpp

#include "data.h" 

template <size_t N> Data<N>::Data() 
: 
    n(N), 
    values() 
{ 
    for (size_t i = 0; i < n; ++i) 
    { 
     values[i] = i; 
    } 
} 

template class Data<1u>; 
template class Data<2u>; 

// list.h

#ifndef LIST_H_ 
#define LIST_H_ 

#include <cstddef> 
#include <memory> 

class List 
{ 
    private: 
     std::shared_ptr<void> data; 
    public: 
     List(const size_t); 
     void printData() const; 
}; 

#endif // LIST_H_ 

// list.cpp

#include "list.h" 

#include <iostream> 
#include <stdexcept> 

#include "data.h" 

List::List(const size_t n) 
: 
    data() 
{ 
    switch (n) 
    { 
     case 1u: 
     data = std::static_pointer_cast<void>(std::make_shared<Data<1u>>()); 
     break; 
     case 2u: 
     data = std::static_pointer_cast<void>(std::make_shared<Data<2u>>()); 
     break; 
     default: 
     throw std::runtime_error("not instantiated.."); 
    } 
} 

void List::printData() const 
{ 
    auto obj = std::static_pointer_cast<Data<>>(data); // my question is about this 
    std::cout << obj->n << ": "; 
    for (size_t i = 0; i < obj->n; ++i) 
    { 
     std::cout << obj->values[i] << " "; 
    } 
    std::cout << "\n"; 
} 

// MAIN.CPP

#include "list.h" 

int main() 
{ 
    for (size_t i = 1; i <= 2; ++i) 
    { 
     try 
     { 
      List list(i); 
      list.printData(); 
     } 
     catch (...) 
     { 
      return 1; 
     } 
    } 
} 

좀이 같은 끔찍한 디자인을 생각할 수 있다는 것을 알고있다. 당신이 화려한 대안을 가지고 있지 않다면, 여기에서 이것을 토론하지 마십시오.

제 질문은 auto obj = std::static_pointer_cast<Data<>>(data);List::printData()에 대한 것입니다. 그것은 안전하지 못하다고 느낍니다. 올바른 인스턴스 생성이 사용됩니까? g++-4.6.3은이 코드에 대해 경고를 표시하지 않으며 예상 값을 인쇄합니다.

+0

데이터 클래스의'const size_t n'이 완전히 불필요하다는 것을 알기를 바랍니다. ** 'N' **과 같은 템플릿 매개 변수 목록의 일부로 값을가집니다. – WhozCraig

+0

@WhozCraig'obj-> n '은 어떨까요? 템플릿 매개 변수를 사용하여이를 수행하는 방법은 무엇입니까? – stefan

+0

std :: static_pointer_cast > 항상 데이터로 전송됩니다 <0> ... 그게 당신이 묻는 것입니까? –

답변

2

예. 안전하지 않아. void*을 통해 전송할 때마다 UB의 위험을 감수해야합니다. 컴파일러는 더 이상 필요한 형식 정보가 없으므로 경고하지 않습니다. 따라서 올바른 유형으로 캐스팅하는 것은 사용자가 수행하는 작업입니다.

기술적으로 말해서 여기에 정의되지 않은 동작이 발생하고 있습니다. 그러나 내 베팅은 일반적으로 효과가있을 것입니다. 그것은 항상 당신이 C에서해야하는 호사스러운 쓰레기와 다르지 않습니다.

작동 원리는 인스턴스의 바이너리 레이아웃이 동일 할 가능성이 높기 때문입니다. 먼저 'n'을 입력하십시오.이 역겨운 트릭을하면 배열의 시작 부분을 따라야합니다.

포인터의 영역 밖에서 이것을 수행한다면 스스로를 망칠 것입니다.

개체가 올바르게 삭제되는 유일한 이유는 shared_ptr이 작성 시점에 기본 삭제자를 작성하므로 올바른 유형을 삭제하는 방법을 알고 있기 때문입니다. 다른 스마트 포인터 중 하나를 사용하면 모든 종류의 BS가 발생합니다.

편집 :

지금,이 일을 더 나은 방법은 크기로 배열 타입 시스템의 사용을 포기하는 것입니다. 런타임 배열을 원한다면 런타임 시스템을 사용하십시오! 당신은 무료 스토어에서 어쨌든 그것을 만들므로, 이런 유형의 시스템을 악용하면 어떤 이점도 얻지 못할 것입니다. 목록 생성자에 전달 된 크기를 기반으로 배열을 할당하는 경우 안전하고 예측 가능한 표준 동작을 수행 할 수 있습니다.

+0

C99와 같은 것이면 다른 유형 (struct sockaddr 및 친구들에게 유용)을 통해 주요 공통 필드에 액세스 할 수 있도록하는 메모리 레이아웃 보장이있을 것입니다. 그러나 저는 아닙니다. 이것이 배열을 위해 작동한다는 것이 보장됩니다 (길이가 0 인 배열은 물론). 어쨌든, 나는 C++이 C99의 가변 길이 배열과 같이 취급하지 않는 한 길이가 0 인 배열을 UB라고 첨언한다. –

+0

@tc - 그래, 나도 몰라. 나는 C++이 그것을 가변 길이 배열처럼 다루지 않을지 의심 스럽다. 실제로 C++에서 배열 액세스에 대한 제한이 있습니다 (예 : 전체 및 배열 [one_past_end] 등). 여하튼, 타입 시스템의 주요 남용이며, 문제를 야기 할 수 있습니다 ... 팀원들이 볼 때 계속해서 WTF 0_o가 될 것이라는 사실 만 있다면. 나는 확실히 그것을 장려하지는 않지만, 대부분은 일반적으로 일할 것이라고 생각합니다. –

+0

컴파일 할 때 필요한 목록 크기를 모두 알아야하기 때문에 실용적이지 않습니다. –

0

당신이 가지고있는 것은 컴파일 타임에 구문 분석되는 static_cast입니다.그래서 컴파일러를 말하고있는 것은 :

auto obj = std::static_pointer_cast<Data<>>(data); 

static_pointer_cast 변수는 datastd::shared_ptr<Data<>>를 입력합니다.
기본적으로 (템플릿 프로토 타입에서 선언 한대로) Data<>Data<0>을 의미합니다.

그러면 항상 같은 유형의 shared_pointer이 표시됩니다.

당신이 할 수있는 일은 인터페이스를 만들고 런타임에 크기를 얻는 것입니다.

class IData 
{ 
    virutal size_t GetDataSize() = 0; 
} 

template <size_t N = 0> 
class Data : public IData 
{ 
    public: 
     const size_t n; 
     size_t values[N]; 
     Data<N>(); 
     virtual size_t GetDataSize() override { return N; } 
}; 

그런 다음 인터페이스 유형에 목록을 유지하고 단지 .cpp 파일에 템플릿 구현을 두지 않는, 게다가 data->GetDataSize();

을 사용하여, 그들이 사용하고 어디에서 볼 수 있어야합니다.

+0

cpp 파일에 템플릿 구현을 두는 것이 그것이 사용 된 유일한 장소라면 완벽하게 적합합니다. –

+0

@CrazyEddie Yeap하지만이 경우에는 사용되지 않습니다. –