Inheriting C ++ classes containing other derived classes

Having the following C ++ classes as a simplification of the problem:

struct Car
{
    virtual int get_price() = 0;
};

struct ExpensiveCar: public Car
{
    int get_price( ) {/*..*/ }
    void apply_turbo( ){/*..*/};
};

struct CheapCar: public Car
{
    int get_price( ) {/*..*/}
};

struct CarRetailer
{
    virtual std::vector<Car*> get_cars( ) = 0;
};


struct ExpensiveCarsRetailer : public CarRetailer
{
    virtual std::vector<Car*> get_cars( ) { /*..*/ }
    std::vector<ExpensiveCar*> get_cars_for_exhibitions( );
};

struct CheapCarsRetailer : public CarRetailer
{
    virtual std::vector<Car*> get_cars( ) { /*..*/ }
    std::vector<CheapCar*> get_second_hand_cars( );
};

Rules: Expensive cars are only sold at ExpensiveCarsRetailers (similar to cheap cars). Cheap cars have no turbo, and expensive cars are not sold manually.

The problem I am facing here is a combination of classes that also contain inherited classes. Therefore, if it is ExpensiveCarRetailerinherited from CarRetailer, it will need to implement virtual std::vector<Car*> get_cars( )that actually returns a vector Car*, however ExpensiveCarRetailer, only objects are created inside ExpensiveCar. It is also get_cars_for_exhibitions()not included in the open interface CarRetailer, so it can return std::vector<ExpensiveCar*>.

API ( Car* ExpensiveCar*) , , , apply_turbo( ) ExpesiveCarsRetailer.

ExpensiveCarsRetailer ferrari;

std::vector<Car*> car = ferrari.get_cars();
ExpensiveCar* expensive_car;
for( int i = 0; i < car.size( ); ++i)
{
expensive_car = dynamic_cast<ExpensiveCar*>(car[i]);
expensive_car->apply_turbo();
}

, , , , ( , , ..) . .

CarRetailer , :

template<typename T>
    struct CarRetailer
    {
        virtual std::vector<T*> get_cars( ) = 0;
    };

:

struct ExpensiveCarRetailer: public CarRetailer<ExpensiveCar>
{
...
}

, , , , CarRetailer ( ) .. ( / ) , CarRetailer, .

+4
2

" , ", . : http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science). :

struct CarRetailer
{
    virtual std::vector<Car*> get_cars( ) = 0;
};

, .. get_cars() , Car, . , , :

CarRetailer* ferrari = new ExpensiveCarsRetailer();
auto niceCars ferrari->get_cars();
niceCars.push_back(new Car{"Trabant"}); // you promised in the declaration that it was possible!

"promises", , , "" . , , , ++ (?) , :

struct CarRetailer
{
    virtual const std::vector<const Car*> get_cars( ) = 0;
};

struct ExpensiveCarsRetailer : public CarRetailer 
{
    const std::vector<const ExpensiveCar*> get_cars( ) = 0;
    // Alas, it won't override
};

(.. , , ++ 17 - ) :

struct CarRetailer
{
    virtual Car* const cars_begin( ) = 0;
    virtual Car* const cars_end( ) = 0;
};

struct ExpensiveCarsRetailer : public CarRetailer 
{
    ExpensiveCar* const cars_begin( ) override {return cars->begin();}
    ExpensiveCar* const cars_end( ) override {return cars->end();}

    private:
    vector<ExpensiveCar>* cars; 
};

(: , , , . , )

, , , ++ <algorithm> . :

any_of(dealer.cars_begin( ), dealer.cars_end( ),
       [](const auto& car) -> bool {return car.hasScratch();}
) ? complain() : congratulate();

, CarDealer *cars , , , , . , , ++ . , vector<Car>*, cars_begin cars_end . , .

, , : IMHO , , .

, , , , , :

struct ExpensiveCarsRetailer /* not derived, not templated */
{
    std::vector<ExpensiveCar> get_cars( ) { /*..*/ }
    // you can also return a vector of pointers or of unique_pointers, as you feel like.
};

struct CheapCarsRetailer /* not derived, not templated */
{
    std::vector<CheapCar> get_cars( );
};

:

template <typename T> print_car_table(T dealer) {
    // This will work on both Expensive and Cheap dealers

    // Not even a hierarchy for the Car classes is needed:
    // they can be independent structs, like the Dealer ones

    auto cars = dealer.get_cars();

    for (const auto& car : cars) { std::cout << car.name() << "\t" << car.color() << "\n"; }
}

template <typename T> apply_turbo(T dealer) {
    // This will work if dealer is an ExpensiveDealer,
    // and fail on compile time if not, as wanted

    auto cars = dealer.get_cars();

    for (auto& car : cars) { car.apply_turbo(); }
}

, , , . , CarMuseum, get_cars(), print_car_table(T), - . , , (class CheapDealer : public HasACarList, public HasAPriceList, /* yuck ...*/)..

, . , vector<Dealer> Dealer* ( , get_cars() ).

:

struct Car {
    virtual int get_price() = 0;
    virtual void apply_turbo( ) = 0;
};

struct CheapCar: public Car
{
    int get_price( ) {/*..*/}
    void apply_turbo( ){throw OperationNonSupportedException();};
};

++, ? , , , . .

, , Scala ( ) . , , .

+1

, , get_cars_for_exhibitions? , , , ExpensiveCarsRetailer, . , , ExpensiveCar Car CarRetailer:

template<typename T>
using Ptrs = std::vector<std::unique_ptr<T>>;

template<class T, class U>
Ptrs<U> upcast(Ptrs<T> input) {
  return Ptrs<U>(std::make_move_iterator(input.begin()),  
                 std::make_move_iterator(input.end()));
}
struct CarRetailer {
  virtual Ptrs<Car> getCars() = 0;
};

struct ExpensiveCarsRetailer : CarRetailer {
  Ptrs<ExpensiveCar> getCarsImpl() { ... }
  Ptrs<Car> getCars() override { return upcast<Car>(getCarsImpl()); }
};

, ExpensiveCar:

ExpensiveCarsRetailer ferrari;

auto expensive_cars = ferrari.getCarsImpl();
for (auto& expensive_car : expensive_cars)
  expensive_car->apply_turbo();

:

CarRetailer* retailer = &ferrari;
auto cars = retailer->getCars();
for (auto& car : cars)
   std::cout << "Car price " << car->get_price() << "\n";
0

Source: https://habr.com/ru/post/1589082/


All Articles