C++ статические виртуальные участники?

method_two не будет работать, потому что Вы определяете функцию членства, но не говорите ее, чего функция является членом. При выполнении последней строки, Вы доберетесь:

>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)

при определении функций членства для класса первый аргумент должен всегда быть 'сам'.

133
задан Null 2 October 2015 в 22:03
поделиться

9 ответов

Как уже говорили другие, есть 2 важных элемента информации:

  1. нет this указателя при вызове статической функции и
  2. этот указатель указывает на структуру, в которой виртуальная таблица или преобразователь используются для поиска вызываемого метода времени выполнения.

Статическая функция определяется во время компиляции.

Я показал этот пример кода в Статические члены C ++ в классе ; он показывает, что вы можете вызвать статический метод с помощью нулевого указателя:

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;
}
-3
ответ дан 24 November 2019 в 00:03
поделиться

Я думаю, что то, что вы пытаетесь сделать, можно сделать с помощью шаблонов. Я пытаюсь читать здесь между строк. Что вы пытаетесь сделать, так это вызвать метод из некоторого кода, где он вызывает производную версию, но вызывающий не указывает, какой класс. Пример:

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

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

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

int main()
{
    Try();
}

Вы хотите, чтобы Try () вызывал Bar-версию M без указания Bar. Для статики это делается с помощью шаблона. Поэтому измените его так:

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

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

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

int main()
{
    Try<Bar>();
}
2
ответ дан 24 November 2019 в 00:03
поделиться

Возможно. Сделайте две функции: статическую и виртуальную

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
};
11
ответ дан 24 November 2019 в 00:03
поделиться

На днях я столкнулся с этой проблемой: У меня было несколько классов, полных статических методов, но я хотел использовать наследование и виртуальные методы и уменьшить повторение кода. Мое решение было:

Вместо использования статических методов используйте синглтон с виртуальными методами.

Другими словами, каждый класс должен содержать статический метод, который вы вызываете для получения указателя на один общий экземпляр класса. . Вы можете сделать настоящие конструкторы частными или защищенными, чтобы внешний код не мог использовать их неправильно, создав дополнительные экземпляры.

На практике использование синглтона очень похоже на использование статических методов, за исключением того, что вы можете воспользоваться преимуществами наследования и виртуальных методов. .

каждый класс должен содержать статический метод, который вы вызываете, чтобы получить указатель на один общий экземпляр класса. Вы можете сделать настоящие конструкторы частными или защищенными, чтобы внешний код не мог использовать их неправильно, создав дополнительные экземпляры.

На практике использование синглтона очень похоже на использование статических методов, за исключением того, что вы можете воспользоваться преимуществами наследования и виртуальных методов. .

каждый класс должен содержать статический метод, который вы вызываете, чтобы получить указатель на один общий экземпляр класса. Вы можете сделать настоящие конструкторы частными или защищенными, чтобы внешний код не мог использовать их неправильно, создав дополнительные экземпляры.

На практике использование синглтона очень похоже на использование статических методов, за исключением того, что вы можете воспользоваться преимуществами наследования и виртуальных методов. .

21
ответ дан 24 November 2019 в 00:03
поделиться

Нет, нет способа сделать это, поскольку что произойдет, когда вы вызовете Object :: GetTypeInformation () ? Он не может знать, какую версию производного класса вызывать, поскольку с ним не связан никакой объект.

Для правильной работы вам придется сделать его нестатической виртуальной функцией; если вы также хотите иметь возможность вызывать версию определенного производного класса не виртуально без экземпляра объекта, вам также необходимо предоставить вторую избыточную статическую не виртуальную версию.

73
ответ дан 24 November 2019 в 00:03
поделиться

Нет, это невозможно, поскольку статические члены связываются во время компиляции, а виртуальные - во время выполнения.

0
ответ дан 24 November 2019 в 00:03
поделиться

Нет, это невозможно, потому что у статических функций-членов отсутствует this указатель. А статические члены (как функции, так и переменные) сами по себе не являются членами класса. Они просто вызываются ClassName :: member и соответствуют спецификаторам доступа к классу. Их хранилище определено где-то вне класса; хранилище не создается каждый раз, когда вы создаете экземпляр объекта класса. Указатели на члены класса имеют особую семантику и синтаксис. Указатель на статический член является обычным указателем во всех отношениях.

виртуальным функциям в классе требуется указатель this , и они очень связаны с классом, поэтому они не могут быть статическими.

8
ответ дан 24 November 2019 в 00:03
поделиться

Многие сказать, что это невозможно,Я бы пошел еще дальше и сказал, что это не имеет смысла.

Статический член - это то, что не имеет отношения ни к какому экземпляру, а только к классу.

Виртуальный член - это то, что не связано напрямую ни с каким классом , только для экземпляра.

Таким образом, статический виртуальный член будет чем-то, что не относится ни к одному экземпляру или какому-либо классу.

56
ответ дан 24 November 2019 в 00:03
поделиться

Возможно!

Но что именно возможно, давайте сузим круг вопросов. Людям часто нужна какая-то «статическая виртуальная функция» из-за дублирования кода, необходимого для возможности вызова одной и той же функции через статический вызов «SomeDerivedClass :: myfunction ()» и полиморфный вызов «base_class_pointer-> myfunction ()». «Законный» метод разрешения такой функциональности - это дублирование определений функций:

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(); }
};

Что делать, если базовый класс имеет большое количество статических функций, а производный класс должен переопределить каждую из них, а один забыл предоставить повторяющееся определение для виртуальной функции. Хорошо, мы получим странную ошибку во время выполнения , которую трудно отследить. Причина дублирования кода - это плохо.Следующее пытается решить эту проблему (и я хочу заранее сказать, что он полностью типобезопасен и не содержит черной магии вроде typeid или dynamic_cast:)

Итак, мы хотим предоставить только одно определение getTypeInformation ( ) на производный класс, и очевидно, что это должно быть определение функции static , потому что невозможно вызвать SomeDerivedClass :: getTypeInformation (), если getTypeInformation () является виртуальным. Как мы можем вызвать статическую функцию производного класса через указатель на базовый класс? Это невозможно с vtable, потому что vtable хранит указатели только на виртуальные функции, и, поскольку мы решили не использовать виртуальные функции, мы не можем изменить vtable в наших интересах. Затем, чтобы иметь возможность получить доступ к статической функции для производного класса через указатель на базовый класс, мы должны каким-то образом сохранить тип объекта в его базовом классе.Один из подходов состоит в том, чтобы сделать базовый класс шаблонным с использованием «любопытно повторяющегося шаблона шаблона», но здесь он не подходит, и мы будем использовать метод, называемый «стирание типа»:

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

Теперь мы можем сохранить тип объекта в базовом классе » Объект "с переменной" хранитель ":

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

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

};

В производном классе хранитель должен быть инициализирован во время построения:

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"; }
};

Добавим синтаксический сахар:

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

Теперь объявления потомков выглядят так:

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"; }
};

использование:

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()

Преимущества :

  1. меньше дублирования кода (но мы должны вызывать OVERRIDE_STATIC_FUNCTIONS в каждом конструкторе)

Недостатки:

  1. OVERRIDE_STATIC_FUNCTIONS в каждом конструктор
  2. память и производительность накладные расходы
  3. повышенная сложность

Открытые проблемы:

1) существуют разные имена для статических и виртуальных функций как здесь разрешить неоднозначность?

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) как неявно вызвать OVERRIDE_STATIC_FUNCTIONS внутри каждого конструктора?

15
ответ дан 24 November 2019 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: