samedi 19 avril 2014

fonction - c ++ modèles, fonctions virtuelles, base vide - Stack Overflow


Still getting the hang of c++ classes, and I'm wondering what is the most run-time-efficient way to accomplish this:


I have a derived class that I want to instantiate once (known at compile-time), but I want to typecast the pointer to a base class pointer and pass that around for the rest of my program to use. That way, if I have a future project where I want to change the instance from animal.dog to animal.cat, all other parts of my code will still be able to call the method animalPtr->eat(), but the actual behavior will be specific to dog or cat. I will only ever have 1 instance of animal.dog or animal.cat, and I will never have an instance of base-class animal.


Maybe doing exactly what I just described is exactly how I need to do it, but I read some online arguments about the pros and cons of having the base class "animal" use virtual functions versus having the base class be a template class (not exactly sure how to even make it into a template in this case....to me, templates classes looked like they were used for creating generic data types for the members, but that is not what I'm needing here).


Any help? Remember that speed-performance is the highest priority for me.




Using a virtual function is the object oriented way to solve this. With the usual implementation of virtual functions, it adds another pointer indirection and makes inlining impossible so if you really, really, really have performance issues and the function is small and called millions of times in a tight loop, then you might have a performance issue.


Use the virtual function approach to start with. If it is a problem (and you have measured that this is indeed the problem with a profiler), then you can look into changing things to a template based approach that might be more efficient.




to implement what you want w/o virtual functions can be done by templating the base class:


template<class Parent>
class BASE {
public:
BASE();
virtual ~BASE();
void func() {
reinterpret_cast<Parent*>(this)->func(); // call parent func
// or
(Parent*)(this)->func();
}
};

You can later typedef the BASE class to a normal class name:


typedef BASE<specific_Parent> myBASE;



It seems to me you are trying to decide between run-time and compile-time polymorphisms. If you do really need run-time polymorphism you have to use virtual functions. But if you don't really need run-time polymorphism, you can use CRTP to achieve the compile-time polymorphism.


This is a very primitive example illustrating the compile-time polymorphism (code on ideone.com):


#include <iostream>

namespace so {
template<typename _t_derived_>
class _animal_ {
private:
using _derived_ = _t_derived_;
public:
void eat() const {
static_cast<_derived_ const *>(this)->eat_impl();
}
};

class _dog_: public _animal_<_dog_> {
friend class _animal_<_dog_> ;
private:
using _base_ = _animal_<_dog_>;
protected:
void eat_impl() const {
std::cout << "dog's eating." << std::endl;
}
};

class _cat_: public _animal_<_cat_> {
friend class _animal_<_cat_> ;
private:
using _base_ = _animal_<_cat_>;
protected:
void eat_impl() const {
std::cout << "cat's eating." << std::endl;
}
};

template<typename _t_animal_>
void feed(_t_animal_ const & _animal) {
std::cout << "feeding an animal: ";
_animal.eat();
}
} // namespace so

int main() {
so::_dog_ dog_;
so::_cat_ cat_;

so::feed(dog_);
so::feed(cat_);

return (0);
}

Program output:


feeding an animal: dog's eating.
feeding an animal: cat's eating.


Still getting the hang of c++ classes, and I'm wondering what is the most run-time-efficient way to accomplish this:


I have a derived class that I want to instantiate once (known at compile-time), but I want to typecast the pointer to a base class pointer and pass that around for the rest of my program to use. That way, if I have a future project where I want to change the instance from animal.dog to animal.cat, all other parts of my code will still be able to call the method animalPtr->eat(), but the actual behavior will be specific to dog or cat. I will only ever have 1 instance of animal.dog or animal.cat, and I will never have an instance of base-class animal.


Maybe doing exactly what I just described is exactly how I need to do it, but I read some online arguments about the pros and cons of having the base class "animal" use virtual functions versus having the base class be a template class (not exactly sure how to even make it into a template in this case....to me, templates classes looked like they were used for creating generic data types for the members, but that is not what I'm needing here).


Any help? Remember that speed-performance is the highest priority for me.



Using a virtual function is the object oriented way to solve this. With the usual implementation of virtual functions, it adds another pointer indirection and makes inlining impossible so if you really, really, really have performance issues and the function is small and called millions of times in a tight loop, then you might have a performance issue.


Use the virtual function approach to start with. If it is a problem (and you have measured that this is indeed the problem with a profiler), then you can look into changing things to a template based approach that might be more efficient.



to implement what you want w/o virtual functions can be done by templating the base class:


template<class Parent>
class BASE {
public:
BASE();
virtual ~BASE();
void func() {
reinterpret_cast<Parent*>(this)->func(); // call parent func
// or
(Parent*)(this)->func();
}
};

You can later typedef the BASE class to a normal class name:


typedef BASE<specific_Parent> myBASE;


It seems to me you are trying to decide between run-time and compile-time polymorphisms. If you do really need run-time polymorphism you have to use virtual functions. But if you don't really need run-time polymorphism, you can use CRTP to achieve the compile-time polymorphism.


This is a very primitive example illustrating the compile-time polymorphism (code on ideone.com):


#include <iostream>

namespace so {
template<typename _t_derived_>
class _animal_ {
private:
using _derived_ = _t_derived_;
public:
void eat() const {
static_cast<_derived_ const *>(this)->eat_impl();
}
};

class _dog_: public _animal_<_dog_> {
friend class _animal_<_dog_> ;
private:
using _base_ = _animal_<_dog_>;
protected:
void eat_impl() const {
std::cout << "dog's eating." << std::endl;
}
};

class _cat_: public _animal_<_cat_> {
friend class _animal_<_cat_> ;
private:
using _base_ = _animal_<_cat_>;
protected:
void eat_impl() const {
std::cout << "cat's eating." << std::endl;
}
};

template<typename _t_animal_>
void feed(_t_animal_ const & _animal) {
std::cout << "feeding an animal: ";
_animal.eat();
}
} // namespace so

int main() {
so::_dog_ dog_;
so::_cat_ cat_;

so::feed(dog_);
so::feed(cat_);

return (0);
}

Program output:


feeding an animal: dog's eating.
feeding an animal: cat's eating.

0 commentaires:

Enregistrer un commentaire