Pergunta

Tenho uma classe para analisar uma matriz que mantém o resultado em um membro do array:

class Parser
{
  ...
  double matrix_[4][4];
};

O usuário desta classe precisa chamar uma função API (como uma função sobre a qual não tenho controle, então não posso simplesmente alterar sua interface para fazer as coisas funcionarem mais facilmente) que se parece com isto:

void api_func(const double matrix[4][4]);

A única maneira que encontrei para o chamador passar o resultado do array para a função é tornar o membro público:

void myfunc()
{
  Parser parser;
  ...
  api_func(parser.matrix_);
}

Esta é a única maneira de fazer as coisas?Estou surpreso com o quão inflexíveis são os arrays multidimensionais declarados como este.Eu pensei matrix_ seria essencialmente o mesmo que um double** e eu poderia lançar (com segurança) entre os dois.Acontece que não consigo nem encontrar um inseguro maneira de lançar entre as coisas.Digamos que eu adicione um acessador ao Parser aula:

void* Parser::getMatrix()
{
  return (void*)matrix_;
}

Isso será compilado, mas não posso usá-lo, porque não parece haver uma maneira de retornar ao tipo de array estranho:

  // A smorgasbord of syntax errors...
  api_func((double[][])parser.getMatrix());
  api_func((double[4][4])parser.getMatrix());
  api_func((double**)parser.getMatrix()); // cast works but it's to the wrong type

O erro é:

erro C2440:'digite elenco':não é possível converter de 'void *' para 'const double [4][4]'

...com um adendo intrigante:

Não há conversões para tipos de array, embora haja conversões para referências ou ponteiros para arrays

Também não consigo determinar como converter para uma referência ou ponteiro para um array, embora isso provavelmente não me ajude aqui.

É certo que neste ponto a questão é puramente académica, uma vez que o void* os elencos dificilmente são mais limpos do que um único membro da classe deixado em público!

Foi útil?

Solução

Aqui está uma maneira agradável e limpa:

class Parser
{
public:
   typedef double matrix[4][4];

   // ...

   const matrix& getMatrix() const
   {
      return matrix_;
   }

   // ...

private:
  matrix matrix_;
};

Agora você está trabalhando com um nome de tipo descritivo em vez de um array, mas como é um typedef o compilador ainda permitirá passá-lo para a função API imutável que usa o tipo base.

Outras dicas

Experimente isso.Ele compila de forma limpa no gcc 4.1.3:

typedef double FourSquare[4][4];

class Parser
{
  private:
    double matrix_[4][4];

  public:
    Parser()
    {
        for(int i=0; i<4; i++)
          for(int j=0; j<4; j++)
            matrix_[i][j] = i*j;
    }

  public:
    const FourSquare& GetMatrix()
    {
        return matrix_;
    }
};

void api_func( const double matrix[4][4] )
{
}

int main( int argc, char** argv )
{
    Parser parser;
    api_func( parser.GetMatrix() );
    return 0;
}

Eu usei uma união como esta para passar matrizes no passado:

union matrix {
    double dflat[16];
    double dmatr[4][4];
};

Em seguida, passe um ponteiro para o seu setter e copie os dados para a matriz da sua classe.

Existem maneiras de lidar com isso de outra forma (que são mais genéricas), mas essa solução tende a ser a mais limpa no final, na minha experiência.

Eu pensei que matriz_ seria essencialmente o mesmo que um duplo**

Em C existem verdadeiros arrays multidimensionais, não arrays de ponteiros para arrays, então um double[4][4] é um array contíguo de quatro arrays double[4], equivalente a um double[16], não um (double *)[4].

Não há conversões para os tipos de matriz, embora haja conversões para referências ou ponteiros para matrizes que lançam um valor para um duplo [4] [4] tentaria construir um na pilha - equivalente a std :: string (parser.getmatrix ( )) - Exceto que a matriz não fornece um construtor adequado.Você provavelmente não queria fazer isso, mesmo que pudesse.

Como o tipo codifica a passada, você precisa de um tipo completo (double[][] não serve).Você pode reinterpretar a conversão de void* para ((double[4][4])*) e então pegar a referência.Mas é mais fácil digitar a matriz e retornar uma referência do tipo correto em primeiro lugar:

typedef double matrix_t[4][4];

class Parser
{
    double matrix_[4][4];
public:
    void* get_matrix () { return static_cast<void*>(matrix_); }

    const matrix_t& get_matrix_ref () const { return matrix_; }
};

int main ()
{
    Parser p;

    matrix_t& data1 = *reinterpret_cast<matrix_t*>(p.get_matrix());

    const matrix_t& data2 = p.get_matrix_ref();
}

Para elaborar a resposta selecionada, observe esta linha

const matrix& getMatrix() const

Isso é ótimo, você não precisa se preocupar com ponteiros e elenco.Você está devolvendo um referência para o objeto de matriz subjacente.As referências IMHO são um dos melhores recursos do C++, que sinto falta ao codificar em C direto.

Se você não está familiarizado com a diferença entre referências e ponteiros em C++, Leia isso

De qualquer forma, você deve estar ciente de que se o Parser objeto que realmente possui o objeto de matriz subjacente sai do escopo, qualquer código que tente acessar a matriz por meio dessa referência agora estará referenciando um objeto fora do escopo e você travará.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top