문제

Java에서는 객체 목록을 가질 수 있습니다. 여러 유형의 객체를 추가 한 다음 검색하고 유형을 확인한 다음 해당 유형에 대한 적절한 작업을 수행 할 수 있습니다.
예 : (사과 코드가 정확하지 않으면 메모리에서 나옵니다)

List<Object> list = new LinkedList<Object>();

list.add("Hello World!");
list.add(7);
list.add(true);

for (object o : list)
{
    if (o instanceof int)
        ; // Do stuff if it's an int
    else if (o instanceof String)
        ; // Do stuff if it's a string
    else if (o instanceof boolean)
        ; // Do stuff if it's a boolean
}

C ++ 에서이 동작을 복제하는 가장 좋은 방법은 무엇입니까?

도움이 되었습니까?

해결책

Boost.variant 및 방문자를 사용한 예 :

#include <string>
#include <list>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost;

typedef variant<string, int, bool> object;

struct vis : public static_visitor<>
{
    void operator() (string s) const { /* do string stuff */ }
    void operator() (int i) const { /* do int stuff */ }
    void operator() (bool b) const { /* do bool stuff */ }      
};

int main() 
{
    list<object> List;

    List.push_back("Hello World!");
    List.push_back(7);
    List.push_back(true);

    BOOST_FOREACH (object& o, List) {
        apply_visitor(vis(), o);
    }

    return 0;
}

이 기술을 사용하는 데있어 한 가지 좋은 점은 나중에 변형에 다른 유형을 추가하고 방문자를 수정하여 해당 유형을 포함시키는 것을 잊어 버리면 컴파일되지 않는다는 것입니다. 너 가지다 가능한 모든 사례를 지원합니다. 반면, 스위치 또는 계단식 IF 문을 사용하는 경우 어디에서나 변경하고 버그를 도입하는 것을 잊기 쉽습니다.

다른 팁

boost::variant Dirkgently의 제안과 유사합니다 boost::any, 그러나 방문자 패턴을 지원하므로 나중에 유형 별 코드를 추가하는 것이 더 쉽습니다. 또한 동적 할당을 사용하지 않고 스택에 값을 할당하여 약간 더 효율적인 코드를 만듭니다.

편집하다: Litb가 주석에서 지적한대로 사용합니다 variant 대신에 any 미리 지정된 유형 목록 중 하나에서만 값을 유지할 수 있음을 의미합니다. 이것은 종종 강점이지만, 대문자의 경우 약점 일 수 있습니다.

다음은 예입니다 (방문자 패턴을 사용하지 않음).

#include <vector>
#include <string>
#include <boost/variant.hpp>

using namespace std;
using namespace boost;

...

vector<variant<int, string, bool> > v;

for (int i = 0; i < v.size(); ++i) {
    if (int* pi = get<int>(v[i])) {
        // Do stuff with *pi
    } else if (string* si = get<string>(v[i])) {
        // Do stuff with *si
    } else if (bool* bi = get<bool>(v[i])) {
        // Do stuff with *bi
    }
}

(그렇습니다. 기술적으로 사용해야합니다 vector<T>::size_type 대신에 int ~을 위한 i유형의 유형은 기술적으로 사용해야합니다 vector<T>::iterator 대신 어쨌든, 나는 그것을 간단하게 유지하려고 노력하고있다.)

C ++는 이종 용기를 지원하지 않습니다.

사용하지 않을 경우 boost 해킹은 더미 수업을 만들고이 더미 클래스에서 모든 다른 클래스를 파생시키는 것입니다. 더미 클래스 객체를 고정하기 위해 선택한 컨테이너를 만들면 갈 준비가되었습니다.

class Dummy {
   virtual void whoami() = 0;
};

class Lizard : public Dummy {
   virtual void whoami() { std::cout << "I'm a lizard!\n"; }
};


class Transporter : public Dummy {
   virtual void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {
   std::list<Dummy*> hateList;
   hateList.insert(new Transporter());
   hateList.insert(new Lizard());

   std::for_each(hateList.begin(), hateList.end(), 
                 std::mem_fun(&Dummy::whoami));
   // yes, I'm leaking memory, but that's besides the point
}

당신이 사용할 경우 boost 당신은 시도 할 수 있습니다 boost::any. 여기 사용의 예입니다 boost::any.

당신은 이것을 훌륭하다고 생각할 수 있습니다 기사 두 명의 주요 C ++ 관심 전문가에 의해.

지금, boost::variant AS를 찾아야 할 또 다른 것입니다 j_random_hacker 말하는. 그래서 여기에 a 비교 무엇을 사용할 것인지에 대한 공정한 아이디어를 얻기 위해.

a boost::variant 위의 코드는 다음과 같습니다.

class Lizard {
   void whoami() { std::cout << "I'm a lizard!\n"; }
};

class Transporter {
   void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {

   std::vector< boost::variant<Lizard, Transporter> > hateList;

   hateList.push_back(Lizard());
   hateList.push_back(Transporter());

   std::for_each(hateList.begin(), hateList.end(), std::mem_fun(&Dummy::whoami));
}

그런 종류의 일이 실제로 얼마나 자주 유용합니까? 나는 C ++로 몇 년 동안, 다른 프로젝트에서 프로그래밍 해 왔으며 실제로 이종 컨테이너를 원하지 않았습니다. 어떤 이유로 든 Java에서 일반적 일 수 있지만 (Java 경험이 훨씬 적습니다) Java 프로젝트에서는 IT를 사용하기 위해서는 C ++에서 더 잘 작동하는 다른 일을 할 수있는 방법이있을 수 있습니다.

C ++는 Java보다 유형 안전성에 중점을두고 있으며 이는 매우 유형이 없습니다.

즉, 물건에 공통점이 없다면 왜 함께 저장하고 있습니까?

그들이 공통점이 있다면, 당신은 그들이 상속 될 수 있도록 수업을 만들 수 있습니다. 또는 Boost :: Any. 그들이 상속하는 경우, 전화 할 가상 기능이 있거나 실제로 필요한 경우 Dynamic_cast <>을 사용하십시오.

나는 유형을 기준으로 분기하기 위해 동적 유형 주조를 사용하여 종종 아키텍처의 결함을 암시한다는 것을 지적하고 싶습니다. 대부분의 경우 가상 기능을 사용하여 동일한 효과를 얻을 수 있습니다.

class MyData
{
public:
  // base classes of polymorphic types should have a virtual destructor
  virtual ~MyData() {} 

  // hand off to protected implementation in derived classes
  void DoSomething() { this->OnDoSomething(); } 

protected:
  // abstract, force implementation in derived classes
  virtual void OnDoSomething() = 0;
};

class MyIntData : public MyData
{
protected:
  // do something to int data
  virtual void OnDoSomething() { ... } 
private:
  int data;
};

class MyComplexData : public MyData
{
protected:
  // do something to Complex data
  virtual void OnDoSomething() { ... }
private:
  Complex data;
};

void main()
{
  // alloc data objects
  MyData* myData[ 2 ] =
  {
    new MyIntData()
  , new MyComplexData()
  };

  // process data objects
  for ( int i = 0; i < 2; ++i ) // for each data object
  {
     myData[ i ]->DoSomething(); // no type cast needed
  }

  // delete data objects
  delete myData[0];
  delete myData[1];
};

안타깝게도 C ++에서는 쉽게 수행 할 수있는 방법이 없습니다. 기본 클래스를 직접 만들고이 수업에서 다른 모든 클래스를 도출해야합니다. 기본 클래스 포인터의 벡터를 작성한 다음 Dynamic_cast (자체 런타임 오버 헤드와 함께 제공)를 사용하여 실제 유형을 찾으십시오.

이 주제의 완전성을 위해서는 Void*를 사용하여 Pure C로 실제로 수행 할 수 있다고 언급하고 싶습니다. 나에게 코드). 객체가 어떤 유형인지 알고 있거나 그에 대한 어딘가에 필드를 저장하는 경우에는 효과가 있습니다. 당신은 가장 확실히 이것을하고 싶지 않지만 여기에 가능하다는 것을 보여주는 예가 있습니다.

#include <iostream>
#include <vector>

using namespace std;

int main() {

  int a = 4;
  string str = "hello";

  vector<void*> list;
  list.push_back( (void*) &a );
  list.push_back( (void*) &str );

  cout <<  * (int*) list[0] << "\t" << * (string*) list[1] << endl;

  return 0;
}

컨테이너에 원시 유형을 저장할 수는 없지만 Java의자가 옥스 원시 유형과 유사한 원시 유형 래퍼 클래스를 생성 할 수 있습니다 (예에서는 원시 유형 리터럴이 실제로 자서전됩니다). 그 인스턴스는 C ++ 코드로 나타나고 (거의) 원시 변수/데이터 부재와 마찬가지로 사용될 수 있습니다.

보다 내장 유형의 객체 포장지 ~에서 C ++의 객체 지향 설계 패턴을 갖는 데이터 구조 및 알고리즘.

래핑 된 객체를 사용하면 C ++ typeid () 연산자를 사용하여 유형을 비교할 수 있습니다. 다음 비교가 작동 할 것이라고 확신합니다.if (typeid(o) == typeid(Int)) int가 Int Primitive 유형 등의 래핑 클래스가되는 곳 ...if (o.get_typeid() == typeid(Int)) ...

즉, 당신의 모범과 관련하여 이것은 나에게 코드 냄새가납니다. 이것이 당신이 객체의 유형을 확인하는 유일한 장소가 아니라면, 나는 다형성을 사용하는 경향이 있습니다 (특히 유형과 관련하여 특정 다른 방법/기능이있는 경우). 이 경우 나는 래핑 된 각 원시 클래스에서 구현 될 연기 방법 ( 'do do the the the the the the the the the the the the the affed a do'for 'do sittue')을 추가하는 원시 포장지를 사용합니다. 이를 통해 컨테이너 반복기를 사용하고 IF 문을 제거 할 수 있습니다 (다시 한 번 유형을 비교 한 경우이를 위해 다형성을 사용하여 연기 된 방법을 설정하는 것은 과잉입니다).

나는 상당히 경험이 없지만 여기에 내가 갈 일이 있습니다.

  1. 조작 해야하는 모든 클래스에 대한 기본 클래스를 만듭니다.
  2. 컨테이너 클래스/ 재사용 컨테이너 클래스를 작성하십시오. (다른 답변을 본 후 개정 -내 이전 요점은 너무 비밀 스럽습니다.)
  3. 비슷한 코드를 작성하십시오.

훨씬 더 나은 솔루션이 가능할 것이라고 확신합니다. 또한 더 나은 설명이 가능할 것이라고 확신합니다. 나는 나쁜 C ++ 프로그래밍 습관이 있다는 것을 배웠기 때문에 코드에 들어 가지 않고 아이디어를 전달하려고 노력했습니다.

이게 도움이 되길 바란다.

사실 외에도, 대부분이 지적했듯이, 당신은 그렇게 할 수 없거나, 더 중요한 것은, 아마도 당신은 정말로 원하지 않습니다.

당신의 모범을 무시하고 실제 예에 더 가까운 것을 고려해 봅시다. 특히, 실제 오픈 소스 프로젝트에서 본 일부 코드. 캐릭터 배열에서 CPU를 모방하려고 시도했습니다. 따라서 OP 코드를 기반으로 문자, 정수 또는 문자열에 대한 포인터가 될 수있는 1 바이트 "OP 코드"가 배열에 들어갑니다. 이를 처리하기 위해서는 많은 비트 홀딩과 관련이있었습니다.

내 간단한 해결책 : 4 개 별도의 스택 <> : 하나는 "opcode"열거와 각각 숯, int 및 문자열 용입니다. 다음으로 Opcode 스택에서 벗어나면 다른 세 가지 중 어느 쪽이 피연산자를 얻을 수 있습니다.

실제 문제를 비슷한 방식으로 처리 할 수있는 가능성이 매우 높습니다.

글쎄, 당신은 기본 클래스를 만들고 그것을 상속하는 클래스를 만들 수 있습니다. 그런 다음 std :: 벡터에 보관하십시오.

짧은 대답은 ... 당신은 할 수 없습니다.

긴 대답은 ... 모두 기본 객체에서 상속되는 자신의 새로운 객체를 정의해야합니다. Java에서 모든 개체는 궁극적으로 "물체"에서 내려갑니다.

C ++의 RTTI (실행 시간 유형 정보)는 항상 힘든, 특히 크로스 컴파일러였습니다.

가장 좋은 옵션은 STL을 사용하고 객체 유형을 결정하기 위해 인터페이스를 정의하는 것입니다.

public class IThing
{
   virtual bool isA(const char* typeName);
}

void myFunc()
{
   std::vector<IThing> things;

   // ...

   things.add(new FrogThing());
   things.add(new LizardThing());

   // ...

   for (int i = 0; i < things.length(); i++)
   {
       IThing* pThing = things[i];

       if (pThing->isA("lizard"))
       {
         // do this
       }
       // etc
   }
}

마이크

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top