Pergunta

EDIT: Esta pergunta é mais sobre engenharia da língua de C ++ em si. Eu usei C ++ como um exemplo para mostrar o que eu queria, principalmente porque eu usá-lo diariamente. Eu não quero saber como ele funciona em C ++, mas abrir uma discussão sobre como ele pode ser feito.

Essa não é a forma como ele funciona agora, essa é a maneira que eu desejo isso poderia ser feito, e que iria quebrar C compability com certeza, mas isso é o que eu penso externo "C" tem tudo a ver .

Quer dizer, em cada função ou método que você declare agora você tem que escrever explícito que o objeto será enviado por referência prefixar o operador de referência sobre ele. Desejo que cada tipo não-POD seriam enviados automaticamente por referência, porque eu uso muito isso, na verdade, para cada objeto que é mais de 32 bits de tamanho, e isso é quase todas as classes da mina.

Vamos exemplificar como é agora, assumir a , b e c ser classes:

class example {
    public:
        int just_use_a(const a &object);
        int use_and_mess_with_b(b &object);
        void do_nothing_on_c(c object);
};

Agora, o que eu desejo:

class example {
    public:
        int just_use_a(const a object);
        int use_and_mess_with_b(b object);
        extern "C" void do_nothing_on_c(c object);
};

Agora, do_nothing_on_c () poderia se comportar exatamente como é hoje.

Isso seria interessante, pelo menos para mim, se sente muito mais clara, e também se você sei todos os parâmetros não-POD está vindo por referência eu acredito que os erros seria o mesmo que se você tivesse para declarar explicitamente-lo.

Um outro ponto de vista para esta mudança, de alguém vindo de C, o operador de referência parece-me uma maneira de obter a variável endereço , essa é a maneira que eu usei para obter ponteiros. Quero dizer, ele é o mesmo operador, mas com diferente semântica em diferentes contextos, não que me sinto um pouco errado pouco para você também?

Foi útil?

Solução

Eu acho que você está perdendo o ponto de ++ semântica C e C ++. Você perdeu o fato de C ++ está correto na passagem (quase) tudo por valor, porque é a forma como é feito em C. Sempre . Mas não só em C, como eu vou mostrar-lhe a seguir ...

Parâmetros Semântica no C

Em C, tudo é passado por valor. "primitivos" e "pods" são passados ??por copiar seu valor. Modificá-los em sua função, eo original não será modificado. Ainda assim, o custo de copiar algumas PODs poderia ser não-trivial.

Quando você usa a notação de ponteiro (a *), você não está passando por referência. Você está passando uma cópia do endereço. Que é mais ou menos o mesmo, apenas com uma diferença sutil:

typedef struct { int value ; } P ;

/* p is a pointer to P */
void doSomethingElse(P * p)
{
   p->value = 32 ;
   p = malloc(sizeof(P)) ; /* Don't bother with the leak */
   p->value = 45 ;
}

void doSomething()
{
   P * p = malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   /* Value of p ? 25 ? 32 ? 42 ? */
}

O valor final de p> valor é 32. Porque p foi aprovada, copiando o valor do endereço. Assim, o p original não foi modificado (eo novo vazou).

Parâmetros Semântica em Java e C sharp

Pode ser surpreendente para alguns, mas em Java, tudo é copiado por valor, também. O exemplo C acima daria exatamente os mesmos resultados em Java. Isso é quase o que quiser, mas você não seria capaz de passar primitiva "por referência / ponteiro" tão facilmente como no C.

Em C #, eles adicionaram a palavra-chave "ref". Ele funciona mais ou menos como a referência em C ++. O ponto é, no C #, você tem que mencioná-lo tanto na declaração da função, e em cada chamada. Eu acho que isso não é o que você quer, mais uma vez.

Parâmetros Semântica em C ++

Em C ++, quase tudo é passado, copiando o valor. Quando você estiver usando nada, mas o tipo de símbolo, você está copiando o símbolo (como é feito em C). É por isso que, quando você está usando o *, você está passando uma cópia do endereço do símbolo.

Mas quando você está usando o &, em seguida, assumir que você está passando o objeto real (seja ele struct, int, ponteiro, qualquer que seja):. A ??referência

É fácil confundir-lo como açúcar syntaxic (ou seja, nos bastidores, ele funciona como um ponteiro, e o código gerado é o mesmo usado para um ponteiro). Mas ...

A verdade é que a referência é mais do que o açúcar syntaxic.

  • ponteiros Ao contrário, autoriza manipular o objeto como se na pilha.
  • ponteiros unline, quando associatied com a palavra chave const, autoriza promoção implícita de um tipo para outro (por meio de construtores, principalmente).
  • ponteiros Ao contrário, o símbolo não é suposto ser NULL / inválido.
  • Ao contrário da "by-cópia", você não está gastando tempo inútil copiar o objeto
  • Ao contrário da "by-cópia", você pode usá-lo como um [out] parâmetro
  • Ao contrário da "by-cópia", você pode usar toda a gama de OOP em C ++ (ou seja, você passar um objeto completo para uma função esperando uma interface).

Assim, referências tem o melhor dos dois mundos.

Vamos ver o exemplo C, mas com uma variação C ++ sobre a função doSomethingElse:

struct P { int value ; } ;

// p is a reference to a pointer to P
void doSomethingElse(P * & p)
{
   p->value = 32 ;
   p = (P *) malloc(sizeof(P)) ; // Don't bother with the leak
   p->value = 45 ;
}

void doSomething()
{
   P * p = (P *) malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   // Value of p ? 25 ? 32 ? 42 ?
}

O resultado é 42, eo velho p vazou, substituído pelo novo p. Porque, ao contrário de código C, não estamos passando uma cópia do ponteiro, mas a referência para o ponteiro, ou seja, o ponteiro em si.

Ao trabalhar com C ++, o exemplo acima deve ser cristal claro. Se não for, então você está faltando alguma coisa.

Conclusão

C ++ é passagem por cópia / valor porque é a forma como tudo funciona, seja em C, em C # ou em Java (mesmo em JavaScript ... :-P ...). E como C #, C ++ tem um operador de referência / palavra-chave, como um bônus .

Agora, tanto quanto eu entendo, você está, talvez, fazer o que eu chamo de meio-jockingly C + , ou seja, C com algumas características limitada C ++.

Talvez a sua solução é usar typedefs (que vai enfurecer o seu C ++ colegas, no entanto, para ver o código poluído por typedefs inúteis ...), mas fazendo isso só vai ofuscar o fato de que você está realmente faltando C ++ lá.

Como disse em outro post, você deve mudar a sua mentalidade de desenvolvimento C (de qualquer) para C ++ Desenvolvimento, ou talvez você deve mover-se para outro idioma. Mas não manter a programação do caminho C com características C ++, porque conscientemente ignorar / ofuscar o poder dos idiomas que você usa, você vai produzir código abaixo do ideal.

Nota: E não passar por cópia mais nada do que primitivos. Você vai castrar sua função de sua capacidade de OO, e em C ++, isso não é o que você quer.

Editar

A questão era um pouco modificado (ver https://stackoverflow.com/revisions/ 146271 / list ). Eu deixei a minha resposta original, e responder às novas perguntas abaixo.

O que você pensa sobre a passagem por referência semântica padrão em C ++? Como você disse, ele iria quebrar a compatibilidade, e você terá passagem por diferentes para primitivas (ie tipos built-in , o que ainda seria passado por cópia) e estruturas / objectos (que seriam passados ??como referências). Você teria que adicionar outro operador no sentido de "passar por valor" (o externo "C" é bastante horrível e já utilizado para outra coisa completamente diferente). Não, eu realmente gosto do jeito que é feito hoje em C ++.

[...] o operador de referência parece-me uma maneira de obter o endereço variável, essa é a maneira que eu usei para obter ponteiros. Quero dizer, ele é o mesmo operador, mas com diferente semântica em diferentes contextos, não que me sinto um pouco errado pouco para você também? Sim e não. Operador >> mudou sua semântica quando usado com fluxos C ++, também. Então, você pode usar o operador + = para substituir strcat. Eu acho que o operador & acostumei porque sua significação como "oposto do ponteiro", e porque não queria usar mais um símbolo (ASCII é limitado, eo operador de escopo :: bem como ponteiro -> mostra que alguns outros símbolos são utilizáveis). Mas agora, se & incomoda, && realmente vai enervar você, como eles adicionaram um unário && em C ++ 0x (uma espécie de super-referência ...). Eu ainda tenho que digerir isso sozinho ...

Outras dicas

A opção de compilador que totalmente muda o significado de uma seção de sons código como uma péssima idéia para mim. Quer começar a usar o C ++ sintaxe ou encontrar um idioma diferente.

Eu prefiro não abusar referências mais, fazendo cada (não qualificado) parâmetro uma referência.

As principais referências razão foram adicionados ao C ++ WAS a operador sobrecarga suporte ; se você quiser "pass-by-reference" semântica, C tinha uma maneira perfeitamente razoável de fazê-lo:. ponteiros

Usando ponteiros deixa claro sua intenção de mudar o valor do objeto pontiagudo, e é possível ver isso por apenas olhando para a chamada de função, você não tem que olhar para a declaração função para ver se ele está usando um referência.

Além disso, veja

Eu quero mudar o argumento, Devo usar um ponteiro ou devo usar uma referência? Eu não sei uma forte razão lógica. Se passar `` não é uma objeto '' (por exemplo, um ponteiro nulo) é aceitável, usando um ponteiro faz sentido. Meu estilo pessoal é usar um ponteiro quando eu quiser modificar uma objeto porque, em alguns contextos que faz com que seja mais fácil de detectar que uma modificação é possível.

do mesmo FAQ .

Sim, eu sou da opinião de que essa é uma sobrecarga muito confuso.

Este é o que a Microsoft tem a dizer sobre a situação:

Não declarações de referência confunda com o uso do endereço-de operador. Quando & identificador é precedido por um tipo, tais como int ou carvão, em seguida, o identificador é declarada como uma referência para o tipo. Quando o & identificador não é precedida por um tipo, o uso é a do endereço-de operador.

Eu não sou realmente grande em C ou C ++, mas eu recebo dores de cabeça maiores classificando para fora os vários usos do * e & em ambas as línguas que eu codificação em assembler.

O melhor conselho é fazer um hábito de pensar sobre o que você realmente quer que aconteça. Passagem por referência é bom quando você não tem um construtor de cópia (ou não quiser usá-lo) e é mais barato para objetos grandes. No entanto, em seguida, mutações no parâmetro são senti fora da classe. Você poderia, em vez passar por referência const - então não há mutações, mas você não pode fazer modificações locais. Passe const por valor para objetos baratos que devem ser lidas somente na função e passam não-const por valor quando você quiser uma cópia que você pode fazer modificações locais para.

Cada permutação (por valor / por referência e const / não-const) tem diferenças importantes que definitivamente não são equivalentes.

Quando você passar por valor, você está copiando dados para a pilha. No caso em que você tem um operador = definido para a estrutura ou classe que você está passando, ele é executado. Não existe qualquer directiva compilador Estou ciente de que iria lavar a ladainha de confusão linguagem implícita de que a mudança proposta seria inerentemente causar.

A melhor prática comum é passar valores por referência const, não apenas por referência. Isso garante que o valor não pode ser alterado na função de chamada. Este é um elemento de uma base de código const-correta.

A base de código totalmente const-correta vai ainda mais longe, acrescentando const para o final de protótipos. Considere o seguinte:

void Foo::PrintStats( void ) const {
   /* Cannot modify Foo member variables */
}

void Foo::ChangeStats( void ) {
   /* Can modify foo member variables */
}

Se você estava a passar um objeto Foo-se para uma função, com o prefixo const, você é capaz de chamar printStats (). O compilador erro fora em uma chamada para ChangeStats ().

void ManipulateFoo( const Foo &foo )
{
    foo.PrintStats();  // Works
    foo.ChangeStats(); // Oops; compile error
}

Eu honestamente acho que toda esta passagem por valor / passar pela ideia de referência em C ++ é enganosa. Tudo é passar por valor. Você tem três casos:

  1. Onde você passar uma cópia local de uma variável

    void myFunct(int cantChangeMyValue)
    
  2. Onde você passar uma cópia local de um ponteiro para uma variável

    void myFunct(int* cantChangeMyAddress) {
        *cantChangeMyAddress = 10;
    }
    
  3. Onde você passar uma 'referência', mas através da magia do compilador é como se você passou um ponteiro e simplesmente desreferenciado-lo cada vez.

    void myFunct(int & hereBeMagic) {
        hereBeMagic = 10; // same as 2, without the dereference
    }
    

Eu, pessoalmente encontrá-lo muito menos confuso para lembrar que tudo é passar por valor. Em alguns casos, esse valor pode ser um endereço, que lhe permite mudar as coisas fora da função.

O que você está sugerindo não permitiria que o programador para fazer número 1. Eu, pessoalmente, acho que seria uma má idéia para tirar essa opção. Uma grande vantagem de C / C ++ é ter uma gestão de memória de granulação fina. Fazendo tudo passagem por referência é simplesmente tentando fazer C ++ mais como Java.

há algo não está claro. quando você diz:

int b (b & param);

o que você pretende para o segundo 'b'? esqueceu-se de introduzir um tipo? esqueceu-se de escrever de forma diferente em relação ao primeiro 'b'? Não acha que é claro para escrever algo como:

class B{/*something...*/};
int b(B& param);

Desde agora, eu suponho que você quer dizer o que eu escrevo.

Agora, sua pergunta é "você não acha que vai ser melhor que o compilador irá considerar cada passagem por valor de um não-POD como pass-by-ref?". O primeiro problema é que ele vai quebrou seu contrato. Eu suponho que você quer dizer passagem por CONST-referência, e não apenas por referência.

A sua questão agora é reduzida a este: "você sabe se há alguns compiladores directiva, que chamada de função pode otimizar por valor"

A resposta agora é "eu não sei".

Eu acho que c ++ se tornar muito confuso se você começar a misturar todo o tipo de parâmetros disponíveis, com suas variações const.

Ele fica rapidamente fora de mão para trak todas as chamadas construtores de cópia, todas as dereferences sobrecarregado e assim por diante.

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