Membros virtuais estáticos em C ++?


140

É possível em C ++ ter uma função de membro que seja statice virtual? Aparentemente, não há uma maneira direta de fazer isso ( static virtual member();é um erro de compilação), mas há pelo menos uma maneira de obter o mesmo efeito?

IE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

Faz sentido usar GetTypeInformation()tanto em uma instância ( object->GetTypeInformation()) quanto em uma classe ( SomeObject::GetTypeInformation()), que podem ser úteis para comparações e vitais para modelos.

As únicas maneiras em que consigo pensar envolvem escrever duas funções / uma função e uma constante, por classe, ou usar macros.

Alguma outra solução?


12
Apenas um comentário secundário: métodos estáticos não são executados em nenhuma instância, o que significa que eles não têm o ponteiro implícito. Dito isto, a constassinatura em um método sinaliza o thisponteiro implícito como constante e não pode ser aplicado a métodos estáticos, pois não possuem o parâmetro implícito.
David Rodríguez - dribeas 01/12/2009

2
@ CVB: Eu seriamente reconsiderar a substituição do seu exemplo com código que não envolve reflexão. Do jeito que está agora, você está meio que confundindo dois problemas separados (embora relacionados). Sim, e eu sei que faz cinco anos e meio desde que você pediu.
Einpoklum

Um dos recursos implicitamente exigidos aqui é fazer com que o compilador verifique se cada objeto em uma hierarquia implementa uma interface específica (onde um ou mais dos métodos são estáticos). Basicamente, uma verificação virtual pura para o método estático faz muito sentido, pois se você esquecer de adicionar o método estático, o compilador deve ter erro. virtual não é a palavra-chave aqui; é mais abstrato que é meio que sinônimo em C ++, exceto neste caso específico. Infelizmente, você não pode fazê-lo com C ++ atualmente.
xryl669

Respostas:


75

Não, não há como fazê-lo, pois o que aconteceria quando você ligasse Object::GetTypeInformation()? Ele não pode saber qual versão de classe derivada chamar, pois não há nenhum objeto associado a ela.

Você precisará torná-la uma função virtual não estática para funcionar corretamente; se você também quiser chamar a versão de uma classe derivada específica praticamente sem uma instância de objeto, precisará fornecer também uma segunda versão não virtual estática redundante.


8
Se você pensa na classe estática (ou nas classes membros estáticos) como um singleton, tudo se torna óbvio - no seu caso, simplesmente deve-se chamar Object :: GetTypeInformation - da mesma maneira que chamar o método virtual regular na instância da classe base . (Claro, se C ++ suportado métodos estáticos virtuais)
susto

13
Esse é um argumento completamente ilusório. Se você usar a classe em vez de um objeto, ela naturalmente usaria a versão dessa classe, em vez de fazer o despacho virtual. Nada de novo lá.
Deduplicator

54

Muitos dizem que não é possível, eu daria um passo adiante e diria que não tem sentido.

Um membro estático é algo que não se relaciona a nenhuma instância, apenas à classe.

Um membro virtual é algo que não se relaciona diretamente a nenhuma classe, apenas a uma instância.

Portanto, um membro virtual estático seria algo que não se relaciona a nenhuma instância ou classe.


42
É perfeitamente significativo em linguagens onde as classes são valores de primeira classe - por exemplo, o Delphi possui isso e também possui métodos "estáticos virtuais".
Pavel Minaev 30/11/2009

4
Exatamente. Uma "função virtual" é (por definição) uma função dinamicamente vinculada , ou seja, é escolhida em tempo de execução, dependendo do tipo dinâmico de um determinado objeto. Portanto, nenhum objeto = nenhuma chamada virtual.
Kos

7
Eu também acho que os virtuais estáticos são significativos. Seria possível definir classes de interface e incluir métodos estáticos que precisam ser implementados na classe derivada.
bkausbk

34
Não é tão significativo para um static virtualmétodo, mas um método static puro virtual é muito significativo em uma interface.
Bret Kuhns

4
É perfeitamente significativo ter um static const string MyClassSillyAdditionalName.
einpoklum

23

Encontrei esse problema outro dia: eu tinha algumas classes cheias de métodos estáticos, mas queria usar herança e métodos virtuais e reduzir a repetição de código. Minha solução foi:

Em vez de usar métodos estáticos, use um singleton com métodos virtuais.

Em outras palavras, cada classe deve conter um método estático que você chama para obter um ponteiro para uma única instância compartilhada da classe. Você pode tornar os verdadeiros construtores privados ou protegidos, para que o código externo não possa usá-lo incorretamente, criando instâncias adicionais.

Na prática, usar um singleton é muito parecido com o uso de métodos estáticos, exceto que você pode tirar proveito dos métodos virtuais e de herança.


Isso vai me custar desempenho - a menos que o compilador possa ter certeza de que: 1. Na verdade, é um singleton e 2. Nada é herdado dele, acho que ele não pode otimizar toda a sobrecarga.
Einpoklum

Se o desempenho desse tipo de coisa o preocupa, o C # provavelmente é o idioma errado para você.
Nate CK

3
Ah, bom argumento. Obviamente, já faz um tempo desde que eu pensei sobre isso desde que escrevi em 2009. Deixe-me colocar de outra maneira: se esse tipo de desempenho o preocupa, talvez você deva evitar completamente o uso da herança. O pôster pediu especificamente métodos virtuais, por isso é estranho que você venha aqui para reclamar sobre a sobrecarga dos métodos virtuais.
Nate CK

15

É possível!

Mas o que exatamente é possível, vamos diminuir. As pessoas geralmente desejam algum tipo de "função virtual estática" devido à duplicação de código necessária para poder chamar a mesma função através da chamada estática "SomeDerivedClass :: myfunction ()" e da chamada polimórfica "base_class_pointer-> myfunction ()". O método "Legal" para permitir essa funcionalidade é a duplicação de definições de função:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

E se a classe base tiver um grande número de funções estáticas e a classe derivada tiver que substituir todas elas e esquecermos de fornecer uma definição duplicada para a função virtual. Certo, obteremos algum erro estranho durante o tempo de execução, o que é difícil de rastrear. Causa duplicação de código é uma coisa ruim. O seguinte tenta resolver este problema (e quero dizer de antemão que é completamente seguro para o tipo e não contém nenhuma magia negra como a do typeid ou do dynamic_cast :)

Portanto, queremos fornecer apenas uma definição de getTypeInformation () por classe derivada e é óbvio que deve ser uma definição de staticfunção porque não é possível chamar "SomeDerivedClass :: getTypeInformation ()" se getTypeInformation () for virtual. Como podemos chamar a função estática da classe derivada através do ponteiro para a classe base? Não é possível com o vtable porque o vtable armazena ponteiros apenas para funções virtuais e, como decidimos não usar funções virtuais, não podemos modificar o vtable para nosso benefício. Então, para poder acessar a função estática da classe derivada através do ponteiro para a classe base, precisamos armazenar de alguma forma o tipo de um objeto dentro de sua classe base. Uma abordagem é tornar a classe base modelada usando "padrão de modelo curiosamente recorrente", mas não é apropriado aqui e usaremos uma técnica chamada "apagamento de tipo":

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

Agora podemos armazenar o tipo de um objeto na classe base "Object" com uma variável "keeper":

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

Em uma classe derivada, o detentor deve ser inicializado durante a construção:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

Vamos adicionar açúcar sintático:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

Agora as declarações dos descendentes se parecem com:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

uso:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

Vantagens:

  1. menos duplicação de código (mas precisamos chamar OVERRIDE_STATIC_FUNCTIONS em todos os construtores)

Desvantagens:

  1. OVERRIDE_STATIC_FUNCTIONS em cada construtor
  2. sobrecarga de memória e desempenho
  3. maior complexidade

Questões em aberto:

1) existem nomes diferentes para funções estáticas e virtuais como resolver a ambiguidade aqui?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) como chamar implicitamente OVERRIDE_STATIC_FUNCTIONS dentro de cada construtor?


+1 por esforço, embora não tenha certeza de que isso seja mais elegante do que apenas delegar a funcionalidade a um singleton com métodos virtuais.
Einpoklum

1
@einpoklum, posso pensar em uma situação em que isso pode ser preferível. Suponha que temos muito código de cliente que já chama métodos estáticos. A mudança de métodos estáticos para um singleton com métodos virtuais exigiria alterações no código do cliente, enquanto a solução apresentada acima não é invasiva.
Alsk

A palavra-chave "virtual" não é necessária para "Foo :: getTypeInformation" e "TypeKeeperImpl :: getTypeInformation".
bartolo-otrit

12

Embora Alsk já tenha dado uma resposta bastante detalhada, gostaria de adicionar uma alternativa, pois acho que sua implementação aprimorada é complicada demais.

Começamos com uma classe base abstrata, que fornece a interface para todos os tipos de objetos:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

Agora precisamos de uma implementação real. Mas, para evitar a necessidade de escrever os métodos estático e virtual, teremos nossas classes de objetos reais herdadas dos métodos virtuais. Obviamente, isso só funciona se a classe base souber acessar a função de membro estático. Portanto, precisamos usar um modelo e passar o nome real da classe de objetos para ele:

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

Finalmente, precisamos implementar nossos objetos reais. Aqui, apenas precisamos implementar a função de membro estático, as funções de membro virtual serão herdadas da classe de modelo ObjectImpl, instanciadas com o nome da classe derivada, para acessar seus membros estáticos.

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

Vamos adicionar um código para testar:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

Adendo (12 de janeiro de 2019):

Em vez de usar a função GetClassNameStatic (), você também pode definir o nome da classe como um membro estático, até mesmo "inline", que o IIRC funciona desde o C ++ 11 (não se assuste com todos os modificadores :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

É possível. Faça duas funções: estática e virtual

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

4
Além disso, métodos estáticos não podem ser const. Simplesmente não faz sentido, em que instância eles não sofrerão mutações?
David Rodríguez - dribeas 01/12/2009

1
Isso é principalmente apenas duplicação de código. A idéia é que as subclasses precisem apenas ter o membro const estático, não precisam ter código acessando-o.
einpoklum

8

Não, isso não é possível, porque as funções de membro estático não possuem um thisponteiro. E membros estáticos (funções e variáveis) não são realmente membros da classe em si. Eles são invocados ClassName::membere aderem aos especificadores de acesso à classe. O armazenamento deles é definido em algum lugar fora da classe; o armazenamento não é criado sempre que você instancia um objeto da classe. Os ponteiros para os alunos são especiais em semântica e sintaxe. Um ponteiro para um membro estático é um ponteiro normal em todos os aspectos.

funções virtuais em uma classe precisam do thisponteiro e são muito acopladas à classe, portanto, elas não podem ser estáticas.


1
Somente funções não estáticas precisam de um this ponteiro. funções estáticas não são específicas para uma instância e não precisariam dela. Portanto, não é por isso que os membros estáticos virtuais são impossíveis.
einpoklum

7

Bem, uma resposta bastante tardia, mas é possível usar o padrão curiosamente recorrente do modelo. Este artigo da wikipedia tem as informações de que você precisa e também o exemplo sob polimorfismo estático.


3

Eu acho que o que você está tentando fazer pode ser feito através de modelos. Estou tentando ler nas entrelinhas aqui. O que você está tentando fazer é chamar um método a partir de algum código, onde ele chama uma versão derivada, mas o chamador não especifica qual classe. Exemplo:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

Você deseja que Try () chame a versão Bar de M sem especificar Bar. A maneira como você faz isso para estática é usar um modelo. Então mude assim:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}

1
Se você recuar 4 espaços no código, poderá formatá-lo automaticamente. Como alternativa, acredito que você pode usar o back tick para atingir o mesmo objetivo em linha.
Chollida

1
Este é o óbvio que eu perdi. Obrigado. Ainda assim, os membros públicos são estranhos.
allesblinkt

M () não é função estática. como é chamado T :: M ()?
DDukDDak99

3

Não, a função de membro estático não pode ser virtual. Já que o conceito virtual é resolvido em tempo de execução com a ajuda do vptr, e o vptr não é membro estático de uma classe. Devido a essa função de membro estático não pode acessar o vptr, o membro estático pode seja virtual.


2
Somente métodos virtuais específicos da instância requerem a tabela de instâncias. Você pode ter uma tabela estática - uma por classe -. E se você deseja que as instâncias conheçam, aponte também da vtable da instância para a vtable de estática da classe.
einpoklum

2

Não é possível, mas isso é apenas por uma omissão. Não é algo que "não faz sentido", como muitas pessoas parecem afirmar. Para ser claro, eu estou falando sobre algo como isto:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

Isso é 100% algo que poderia ser implementado (apenas não foi) e eu argumentaria algo que é útil.

Considere como as funções virtuais normais funcionam. Remova os se staticadicione outros itens e temos:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

Isso funciona bem e, basicamente, o que acontece é que o compilador cria duas tabelas, chamadas VTables, e atribui índices às funções virtuais como esta

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

A seguir, cada classe com funções virtuais é aumentada com outro campo que aponta para sua VTable, portanto, o compilador basicamente as altera para ficar assim:

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

Então o que realmente acontece quando você liga b->sayMyName()? Basicamente isso:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(O primeiro parâmetro se torna this .)

Tudo bem, então como ele funcionaria com funções virtuais estáticas? Bem, qual é a diferença entre funções membro estáticas e não estáticas? A única diferença é que os últimos recebem um thisponteiro.

Podemos fazer exatamente o mesmo com funções virtuais estáticas - basta remover o thisponteiro.

b->vtable[Base_Virtual_Functions::sayMyName]();

Isso poderia suportar as duas sintaxes:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

Portanto, ignore todos os opositores. Ele faz sentido. Por que não é suportado então? Eu acho que é porque tem muito pouco benefício e pode até ser um pouco confuso.

A única vantagem técnica sobre uma função virtual normal é que você não precisa passar this para a função, mas não acho que isso faria alguma diferença mensurável no desempenho.

Isso significa que você não tem uma função estática e não estática separada para casos em que você tem uma instância e quando você não tem uma instância, mas também pode ser confuso que seja realmente "virtual" quando você usa a chamada da instância.


0

Não, não é possível, pois os membros estáticos são vinculados em tempo de compilação, enquanto os membros virtuais são vinculados em tempo de execução.


0

Primeiro, as respostas estão corretas: o que o OP está solicitando é uma contradição em termos: métodos virtuais dependem do tipo de tempo de execução de uma instância; funções estáticas especificamente não dependem de uma instância - apenas de um tipo. Dito isto, faz sentido ter funções estáticas retornando algo específico a um tipo. Por exemplo, eu tinha uma família de classes MouseTool para o padrão State e comecei a ter cada uma delas uma função estática retornando o modificador de teclado que a acompanhava; Usei essas funções estáticas na função de fábrica que criaram a instância correta do MouseTool. Essa função verificou o estado do mouse em relação a MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), etc. e, em seguida, instancia o apropriado. É claro que mais tarde eu queria verificar se o estado estava certo, então eu queria escrever algo como "

Portanto, se você estiver querendo isso, talvez queira refazer sua solução. Ainda assim, entendo o desejo de ter métodos estáticos e os chamo dinamicamente com base no tipo dinâmico de uma instância. Eu acho que o padrão de visitantes pode dar o que você deseja. Dá o que você quer. É um pouco de código extra, mas pode ser útil para outros visitantes.

Veja: http://en.wikipedia.org/wiki/Visitor_pattern para obter mais informações.

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

struct SomeObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

Então, para cada objeto concreto:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

e defina o visitante base:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

Em seguida, o visitante concreto que seleciona a função estática apropriada:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

finalmente, use-o:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

Notas:

  • Constness deixado como um exercício.
  • Você retornou uma referência de uma estática. A menos que você tenha um singleton, isso é questionável.

Se você deseja evitar erros de copiar e colar, em que um dos métodos de visita chama a função estática incorreta, você pode usar uma função auxiliar de modelo (que não pode ser virtual) para o visitante com um modelo como este:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};

métodos estáticos virtuais, se eles existissem, não dependeriam de nada em uma instância - mas a instância precisaria saber seu tipo para invocá-los. Isso pode ser resolvido por um compilador (por exemplo, usando alguma estrutura de dados única por classe com ponteiros para métodos e membros estáticos virtuais). Certamente não é uma contradição em termos.
einpoklum

Se é ou não uma contradição em termos, é uma questão de semântica. Pode-se imaginar C ++ permitindo chamar estática de uma instância (por exemplo, em Foo foo; ... foo::bar();vez de Foo::bar();). Isso não é diferente, decltype(foo)::bar();mas isso seria estatisticamente vinculado. A abordagem do visitante parece uma maneira razoável de obter esse comportamento sem tornar o método estático um método const virtual.
Ben

0

Com o c ++, você pode usar herança estática com o método crt. Por exemplo, é amplamente utilizado no modelo de janela atl & wtl.

Consulte https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Para ser simples, você tem uma classe que é modelada por si mesma, como myclass de classe: public myancestor. A partir deste ponto, a classe myancestor agora pode chamar sua função estática T :: YourImpl.


-1

Talvez você possa tentar minha solução abaixo:

class Base {
public:
    Base(void);
    virtual ~Base(void);

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}

Sim, eu sei, 4 anos. Explicando a pontuação-para aqueles que não querem ler o código com tantos detalhes. Base::mSelfrefere-se à instância MAIS RECENTEMENTE construída de qualquer classe derivada, mesmo que essa instância tenha sido destruída . Então, o class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */que NÃO é o que se deseja.
Jesse Chisholm

-3

Como outros já disseram, existem duas informações importantes:

  1. não há this ponteiro ao fazer uma chamada de função estática e
  2. o thisponteiro aponta para a estrutura em que a tabela virtual, ou thunk, é usada para procurar qual método de tempo de execução chamar.

Uma função estática é determinada em tempo de compilação.

Eu mostrei este exemplo de código nos membros estáticos do C ++ na classe ; mostra que você pode chamar um método estático, dado um ponteiro nulo:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}

6
Tecnicamente, esse é um comportamento indefinido. Você não pode deferir um ponteiro nulo por qualquer motivo. As únicas coisas que você pode fazer com um ponteiro nulo é: a) atribuir outro ponteiro a ele eb) compará-lo com outro ponteiro.
30511 KeithB

1
Além disso, você só pode compará-lo em termos de igualdade (ou desigualdade_ com outro ponteiro, sem fazer pedidos. Ou seja p < null, p >= nulletc , todos são indefinidos também # #
Pavel Minaev

1
@ KeithB - ​​Para completar, você também pode chamar com segurança excluir em um ponteiro nulo.
9788 Steve Rowe #
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.