2015-05-23 3 views
0

Скажите, что я хочу определить переменную-член в родительском классе и установить ее значение в унаследованном классе. Возможно, они идентифицируют функциональность, доступную в классе или характер дочернего класса. Например:Переопределить постоянную переменную-член в унаследованном классе

class A 
{ 
public: 
    inline int getX() { return x; } 
protected: 
    const int x = 0; 
}; 

class B : public A 
{ 
protected: 
    const int x = 10; 
}; 

class C : public A 
{ 
protected: 
    const int x = 50; 
}; 

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

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

+1

С ** переменной предназначена для выявления характера унаследованных классов **, это делает намного больше смысла использовать полиморфизм. Это основная причина, по которой она была «изобретена». Другими словами, объявляйте 'virtual int getX()' и реализуйте эту функцию в каждом классе или просто используйте RTTI ('dynamic_cast') для каждого объекта, чтобы« обнаружить его природу ». –

+0

@barakmanos. Часть цели заключалась в том, чтобы сделать каждую попытку, чтобы функция была встроена, так как это будет вызвано много. Этот способ является стандартным, но учитывая, как часто это будет использоваться в том, что я делаю, даже небольшое сокращение накладных расходов стоит того. Поскольку виртуальные члены обычно не будут встроены, я хотел сохранить реализацию в базовом классе. Основываясь на проверке сборки (на которую я не претендую на то, чтобы быть экспертом), кажется, по крайней мере, для моего простого тестового примера, эта функция будет встроена таким образом. –

+0

@WilliamKappler важно, чтобы B & C получил от A, или вы хотите, чтобы у них был подобный интерфейс? Если последнее, есть «более быстрый» способ сделать это, что я могу вам показать. –

ответ

2

В то время как во время компилятора, пытаясь убедиться, что мой примерный код имеет смысл, я действительно натолкнулся на то, что способ, которым я пытался определить константы, был C++ 11. Это заставило меня заглянуть в то, как это было сделано раньше, и я нашел this question, который косвенно коснулся этого вопроса.

Определение переменной таким образом, должно быть сделано, имея базовый класс взять аргумент в его конструктор, в виде:

class A 
{ 
public: 
    A(const int& type) : x(type) {} 
    inline int getX() { return x; } 
protected: 
    const int x; 
}; 

class B : public A 
{ 
public: 
    B() : A(10) {} 
}; 

class C : public A 
{ 
public: 
    C() : A(50) {} 
}; 

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

+0

Также не помешает make 'getX()' член const. 'A :: A()' не нужно принимать ссылку в этом случае. – Peter

0

Чтобы продемонстрировать то, что я сделал в своем комментарии, вот пример того, что, как я думаю, вы пытаетесь сделать (выведено из комментариев).

Я представил как утиные, так и полиморфные решения в одной и той же программе с таймированным проходом через каждый.

Я использую 10 миллионов выборок каждого, чтобы устранить шум кеша памяти.

Вы заметите, что время выполнения полиморфного решения значительно меньше времени выполнения решения, установленного в утке.

#ifdef _WIN32 
#include <Windows.h> 

double get_cpu_time(){ 
    FILETIME a,b,c,d; 
    if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0){ 
     // Returns total user time. 
     // Can be tweaked to include kernel times as well. 
     return 
     (double)(d.dwLowDateTime | 
       ((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001; 
    }else{ 
     // Handle error 
     return 0; 
    } 
} 
#else 
#include <sys/time.h> 

inline double get_cpu_time() noexcept { 
    return (double)clock()/CLOCKS_PER_SEC; 
} 

#endif 


#include <iostream> 
#include <vector> 
#include <memory> 

struct A 
{ 
    A(bool copy_) : copy{copy_} {} 

    virtual ~A() = default; 
    const bool copy = false; 
}; 

struct RealA : public A 
{ 
    RealA() : A { false } {} 
}; 

struct CopyA : public A 
{ 
    CopyA() : A { true } {} 
}; 

// A Thing holder will hold any object which has an interface supports do_something_to(T& thing) 

struct AHolder { 

    template<class Thing> 
    AHolder(std::unique_ptr<Thing> ptr) 
    : _ptr { std::move(ptr) } 
    { 

    } 

    template<class Thing, class...Args> 
    static AHolder construct(Args&&...args) 
    { 
     return AHolder { std::make_unique<model<Thing>>(std::forward<Args>(args)...) }; 
    } 

    void do_something() const { 
     _ptr->do_something(); 
    } 
private: 
    struct concept { 
     virtual ~concept() = default; 

     virtual void do_something() = 0; 
    }; 
    template<class Thing> struct model : concept { 
     template<class...Args> 
     model(Args&&...args) : _thing { std::forward<Args>(args)... } {} 
    private: 
     void do_something() override { 
      do_something_to(_thing); 
     } 
     Thing _thing; 
    }; 

    std::unique_ptr<concept> _ptr; 
}; 


using namespace std; 

size_t copies_processed = 0; 
size_t reals_processed = 0; 

void do_something_to(const CopyA&) 
{ 
    // simulate work 
    ++copies_processed; 
} 

void do_something_to(const RealA&) 
{ 
    // simulate work 
    ++reals_processed; 
} 


int main(int argc, char **argv) { 

    std::vector<std::unique_ptr<A>> duck_typing; 
    std::vector<AHolder> polymorphic; 

    constexpr size_t samples = 10000000; 

    for (size_t i = 0 ; i < samples ; ++i) { 
     if (i % 2) { 
      duck_typing.push_back(make_unique<RealA>()); 
      polymorphic.emplace_back(AHolder::construct<RealA>()); 
     } 
     else { 
      duck_typing.push_back(make_unique<CopyA>()); 
      polymorphic.emplace_back(AHolder::construct<CopyA>()); 
     } 
    } 

    auto duck_start = get_cpu_time(); 
    // nasty duck-typing solution 
    for (const auto& ptr : duck_typing) { 
     if (ptr->copy) { 
      do_something_to(*(static_cast<CopyA*>(ptr.get()))); 
     } 
     else { 
      do_something_to(*(static_cast<RealA*>(ptr.get()))); 
     } 
    } 
    auto duck_stop = get_cpu_time(); 

    auto poly_start = get_cpu_time(); 
    for (const auto& a_like : polymorphic) { 
     a_like.do_something(); 
    } 
    auto poly_stop = get_cpu_time(); 

    cout << "duck typing : " << duck_stop - duck_start << endl; 
    cout << "polymorphic : " << poly_stop - poly_start << endl; 

    cout << "copies processed : " << copies_processed << endl; 
    cout << "reals processed : " << reals_processed << endl; 

    return 0; 
} 

пример вывод:

duck typing : 0.162985 
polymorphic : 0.137561 
copies processed : 10000000 
reals processed : 10000000 
+0

Я ценю, что вы приложили немало усилий, но я думаю, что вам не хватает смысла в том, что я делаю, что понятно, поскольку я не объяснил все это. В основном, мои вызовы для обоих классов идентичны и лишены проверки типа, а реализация функций классов классов просто ничего не делает. Тем не менее, есть много случаев, когда мне нужно убедиться, что я имею дело только с фактическими классами - иначе, например, проскальзывание завершится ошибкой и может вызвать неожиданное поведение. Другие примеры включают индексирование (хранящееся в разных контейнерах) и сериализацию (фактические объекты имеют больше данных). –

+1

@WilliamKappler Конечно, я полностью понимаю. Оба наших примера тривиальны. Я просто хотел продемонстрировать, что, хотя кажется противоречивым, полиморфные решения быстрее, чем решения if-then-else, когда наборы данных велики, потому что случайность данных очень часто приводит к сбою кеша. Полиморфные решения не страдают от проблемы кэширования команд, потому что нет никаких условных переходов, а значения косвенных переходов остаются кешированными. Техника, которую я использовал, называется «полиморфизм как деталь реализации» - очень мощный. –

Смежные вопросы