Pergunta

Eu quero saber se é possível deixar compilador emitir um aviso / erro para o código da seguinte forma:

Nota:

1. Sim, é um estilo ruim programação e devemos evitar tais casos -, mas estamos lidando com código legado e esperança compilador pode ajudar a identificar esses casos para nós)

.

2. Eu prefiro uma opção do compilador (VC ++) para desativar ou ativar objeto de corte, se houver alguma.

class Base{};
class Derived: public Base{};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
Func(Derived());

Aqui, se eu comente a segunda função, a primeira função seria chamado -. E o compilador (ambos VC ++ e GCC) se sente confortável com isso

É padrão C ++? e eu posso pedir compilador (VC ++) para me dar um aviso quando conheceu esse código?

Muito obrigado !!!

Editar:

Obrigado a todos muito por sua ajuda!

Não consigo encontrar uma opção de compilador para dar um erro / aviso - Eu mesmo postei isso no fórum MSDN para VC ++ consultor compilador sem resposta. Então, eu tenho medo nem gcc nem vc ++ implementado esse recurso.

Então adicione construtor que tomar as classes derivadas como paramter seria a melhor solução para agora.

Editar

Eu tenho enviar uma feedbak para MS e espero que eles vão corrigi-lo em breve:

https://connect.microsoft.com/VisualStudio/feedback/ ViewFeedback.aspx? FeedbackID = 421579

-Baiyan

Foi útil?

Solução

Se você pode modificar a classe base que você poderia fazer algo como:

class Base
{
public:
// not implemented will cause a link error
    Base(const Derived &d);
    const Base &operator=(const Derived &rhs);
};

Dependendo do seu compilador que deverá fazê-lo a unidade de tradução, e talvez a função onde o corte está acontecendo.

Outras dicas

Como uma variação de de Khosravian resposta , vou sugerir o uso de construtores de cópia templated e operadores de atribuição. Desta forma, você não precisa saber todas as classes derivadas de uma determinada classe base, a fim de salvaguarda que a classe base contra corte:

class Base
{
private:   // To force a compile error for non-friends (thanks bk1e)
// Not implemented, so will cause a link error for friends
    template<typename T> Base(T const& d);
    template<typename T> Base const& operator=(T const& rhs);

public:
// You now need to provide a copy ctor and assignment operator for Base
    Base(Base const& d) { /* Initialise *this from d */ }
    Base const& operator=(Base const& rhs) { /* Copy d to *this */ }
};

Embora isso reduz a quantidade de trabalho necessária, com essa abordagem, você ainda precisa de mexer com cada classe de base, a fim de protegê-lo. Além disso, ele vai causar problemas se houver conversões legítimos de Base para SomeOtherClass que empregam um membro operator Base() de SomeOtherClass. (Nesse caso, pode ser usada uma solução mais elaborada envolvendo boost::disable_if<is_same<T, SomeOtherClass> >.) Em qualquer caso, você deve remover este código, uma vez que você tenha identificado todas as instâncias do objeto de corte.

Para os implementadores do compilador do mundo: Testing para o objeto de corte é definitivamente algo que estaria criando pena advertências (opcional) para! Eu não consigo pensar em uma instância onde seria o comportamento desejado, e é muito comumente visto em novato C ++ código.

[EDIT 27/3/2015:] Como apontado por Matt McNab, você realmente não precisa declarar o construtor de cópia e operador de atribuição explicitamente como eu fiz acima, como eles vai ainda ser declarado implicitamente pelo compilador. No padrão 2003, C ++, este é explicitamente mencionado na nota sob 106 12,8 / 2:

Como um construtor de modelo não é um construtor de cópia, a presença de tal modelo a não suprimir faz a declaração implícita de um construtor de cópia. construtores modelo participar de resolução de sobrecarga com outros construtores, incluindo construtores de cópia e um construtor modelo pode ser usado para copiar um objeto se ele fornece um jogo melhor do que outros construtores.

Gostaria de sugerir a adição de um construtor para sua classe base que leva uma referência const para a classe derivada de forma explícita (com uma declaração para a frente). No meu aplicativo de teste simples, esse construtor é chamado no caso de corte. Você poderia, então, pelo menos, obter uma declaração de tempo de execução, e você provavelmente poderia obter um asserção em tempo de compilação com uso inteligente de modelos (por exemplo: instanciar um modelo de uma maneira que gera uma afirmação em tempo de compilação em que construtor). Também pode haver formas específicas do compilador para obter avisos de tempo de compilação ou erros quando você chamar funções explícitas; por exemplo, você pode usar "__declspec (reprovado)" para o "construtor fatia" no Visual Studio para receber um aviso em tempo de compilação, pelo menos no caso da função chamada.

Assim, no seu exemplo, o código ficaria assim (para Visual Studio):

class Base { ...
    __declspec(deprecated) Base( const Derived& oOther )
    {
        // Static assert here if possible...
    }
...

Isso funciona no meu teste (aviso em tempo de compilação). Note que isso não resolve o caso de cópia, mas um operador de atribuição semelhante construído deve fazer o truque lá.

Espero que isso ajude. :)

Isto é comumente chamado objeto Slicing e é um problema bastante conhecido para ter a sua próprio artigo Wikipedia (embora seja apenas uma breve descrição do problema).

Eu acredito que eu tenho usado um compilador que tinha um aviso de que você poderia permitir detectar e alertar sobre isso. No entanto, não me lembro qual era.

Não é realmente uma solução para o seu problema imediato, mas ....

A maioria das funções que recebem classe / struct objetos como parâmetros devem declarar os parâmetros como sendo do tipo "const X &" ou "X &", a menos que tenham uma muito boa razão para não.

Se você sempre faz isso, objeto de corte nunca será um problema (referências não se cortado!).

A melhor maneira de combater este problema é, geralmente, para seguir a recomendação de Scott Meyer (veja Effective C ++ ) de só ter classes concretas nos nós folha da sua árvore de herança e garantir que as classes não-folha são abstrato por ter pelo menos uma função virtual pura (o destruidor, se nada mais).

É surpreendente como muitas vezes esta abordagem ajuda a esclarecer a concepção de outras formas, também. O esforço de isolar uma interface abstrata comum é geralmente um esforço de design vale a pena em qualquer caso.

Editar

Embora eu originalmente não deixar isso claro, a minha resposta vem do fato de que não é possível avisar com precisão sobre o objeto de corte em tempo de compilação e por esta razão que pode levar a uma falsa sensação de segurança se você tiver um compilação afirmação de tempo, ou um aviso do compilador habilitado. Se você precisa saber mais sobre casos de corte objeto e necessidade de corrigi-los, em seguida, isso implica que você tem o desejo ea capacidade de alterar o código legado. Se este for o caso, então eu acredito que você deve considerar seriamente a refatoração a hierarquia de classes como uma forma de tornar o código mais robusto.

Meu raciocínio é o seguinte.

Considere alguns código da biblioteca que define uma classe Concrete1 e usa-lo na inferface para esta função.

void do_something( const Concrete1& c );

Passando o tipo seja referência é para a eficiência e é, em geral, uma boa idéia. Se a biblioteca considera Concrete1 ser um valor digite a implementação pode decidiu fazer uma cópia do parâmetro de entrada.

void do_something( const Concrete1& c )
{
    // ...
    some_storage.push_back( c );
    // ...
}

Se o tipo de objeto da referência passou é, na verdade, Concrete1 e não algum outro tipo derivado, em seguida, este código é bom, nenhum corte é realizado. A advertência geral sobre esta função push_back invocação pode produzir apenas falsos positivos e provavelmente seria inútil.

Considere alguns código de cliente que deriva Concrete2 de Concrete1 e passa-lo em outra função.

void do_something_else( const Concrete1& c );

Como o parâmetro é tomado por referência não corte ocorre aqui no parâmetro para verificação, por isso não seria correto para avisar aqui de corte, pois pode ser que nenhum corte ocorre. Passando em um tipo derivado para uma função que leva uma referência ou ponteiro é uma comum e uma maneira útil para tirar proveito dos tipos polimórficos tão aviso ou não permitir isso parece contraproducente.

Então, onde está há erro? Bem, o 'erro' é passar em uma referência a algo que é derivada de uma classe que é então tratado como se fosse um tipo de valor pela função chamada.

Não é, em geral, não há maneira de gerar um aviso consistentemente útil tempo de compilação contra o objeto corte e é por isso que a melhor defesa, sempre que possível, é eliminar o problema por design.

class Derived: public Base{};

Você está dizendo Derivado é uma base, e assim que deve funcionar em qualquer função que recebe uma base. Se este é um problema real, talvez herança não é o que você realmente quer estar usando.

Eu modifiquei o código ligeiramente:

class Base{
  public:
    Base() {}
    explicit Base(const Base &) {}
};

class Derived: public Base {};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
int main() {
  Func(Derived());
}

A palavra-chave explícita irá certificar-se que o construtor não será usado como um operador de conversão implícita -. Você tem que chamá-lo de forma explícita quando você quiser usá-lo

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