Pergunta

Suponha que eu tenha um tipo de dados enum TreeTypes { TallTree, ShortTree, MediumTree }.

E eu tenho que inicializar alguns dados com base em um tipo de árvore específico.

Atualmente, escrevi este código:

int initialize(enum TreeTypes tree_type) {
    if (tree_type == TallTree) {
        init_tall_tree();
    }
    else if (tree_type == ShortTree) {
        init_short_tree();
    }
    else if (tree_type == MediumTree) {
        init_medium_tree();
    }
    return OK;
}

Mas isso é algum tipo de repetição estúpida de código. Não estou usando nenhum dos poderosos recursos de C ++, como modelos.

Como eu poderia escrever melhor este código?

Obrigado, Boda Cydo.

Foi útil?

Solução

Seu código é bom para dois ou três valores, mas você está certo, você precisa de algo mais força industrial quando tem centenas deles. Duas soluções possíveis:

  • Use uma hierarquia de classes, não enums - você pode usar funções virtuais e fazer com que o compilador resolva qual função real chama

  • Crie um mapa de enum -> função, que você inicializa na startup - sua função chama e se torna algo como map[enum]->func()

Os modelos não funcionam tão bem aqui, porque você está tentando tomar uma decisão em tempo de execução, enquanto os modelos fazem suas coisas em tempo de compilação.

Outras dicas

Em uma palavra: herança

class Tree { public: virtual void initialize() = 0; }

class ShortTree : public Tree {
public:
    virtual void initialize(){
        /* Short Tree specific code here */
    }
}

class MediumTree : public Tree {
public:
    virtual void initialize(){
        /* Medium Tree specific code here */
    }
}

class TallTree : public Tree {
public:
    virtual void initialize(){
        /* Tall Tree specific code here */
    }
}

Então, onde quer que você queira chamar Initialize, certifique -se de ter um ponteiro ou uma referência para que o polimorfismo funcione corretamente:

Vector<Tree*> trees;
trees.push_back(new SmallTree());
trees.push_back(new MediumTree();
trees.push_back(new TallTree();

// This will call the tree specific code for each tree in the vector
for(vector<Tree*>::iterator tree = trees.begin(); tree!=trees.end(); ++tree)
    tree->initialize();

Use uma tabela de pesquisa indexada pelos valores da enumeração (assumindo que todas as funções tenham a mesma assinatura), ou seja::

enum TreeTypes { TallTree, ShortTree, MediumTree, MaxTreeTypes }

typedef void (*p_init_func)(void); 

p_init_func initialize_funcs[MaxTreeTypes] =
{
    &init_tall_tree, 
    &init_short_tree,
    &init_medium_tree
};

int initialize(enum TreeTypes tree_type)
{ 
    initialize_funcs[tree_type]();
    return OK; 
} 

Experimente uma instrução Switch:

int initialize(enum TreeTypes tree_type) {
    switch (tree_type) {
        case TallTree: 
            init_tall_tree();
            break;
        case ShortTree:
            init_short_tree();
            break;
        case MediumTree:
            init_medium_tree();
            break;
    }
    return OK;
}

Se essa inicialização for realmente a única distinção, não tenho certeza de que nenhum outro idioma melhorasse a situação.

Você pode subclasse da árvore e criar o tipo certo de objeto de árvore ... mas ainda precisaria diferenciar qual deles instanciar, para que você ainda acabasse com um bloco IF/else/else, em algum lugar.

Dito isto, se houver mais do que apenas a inicialização que seja diferente, você deve subclasse e usar funções virtuais para aprovar as diferenças entre elas.

E o modo de modelo desde que você apontou em suas tags:

enum TreeTypes { Tall, Short, Medium };

struct TreeBase {
    // (...)
};

struct TallTree : public TreeBase {
    // (...)
};

struct ShortTree : public TreeBase {
    // (...)
};

struct MediumTree : public TreeBase {
    // (...)
};

template<TreeTypes N_type = Tall>
struct Tree : public TallTree {
    // (...)
};

template<>
struct Tree<Short> : public ShortTree {
    // (...)
};

template<>
struct Tree<Medium> : public MediumTree {
    // (...)
};

Dessa forma, você obteve classes separadas para cada tipo de árvore que pode ser acessado pelo ponteiro base. Envolvendo -os na classe de árvores, deixe você fazer isso:

Tree<Tall> tall_tree;
Tree<Short> short_tree;
Tree<Medium> medium_tree;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top