3

In the below code, I am trying to override the price() function for both Taxed and Untaxed, however, they both call the parent virtual function instead of the overriden one that was given to them. What did I mess up?

Header file:

#ifndef PRODUCT_H
#define PRODUCT_H
#include <string>
#include <iostream>

class Product {

protected:
    int _quantity;
    double _cost;
    std::string _name;

public:
    Product(std::string name, double cost);

    virtual ~Product();

    void set_quantity(int quantity);

    virtual double price() const;

    friend std::ostream & operator<< (std::ostream &ost, const Product &product);
};

std::ostream & operator<< (std::ostream &ost, const Product &product);

class Taxed : public Product{

private:
    static double _tax;

public:
    using Product::Product;

    virtual ~Taxed();

    static void set_tax_rate(double sales_tax);

    double price() const override;
};

class Taxfree : public Product{
public:
    using Product::Product;

    virtual ~Taxfree();

    double price() const override;
};


#endif //PRODUCT_H

.cpp file:

#include <string>
#include <iomanip>
#include <iostream>
#include "product.h"

Product::Product(std::string name, double cost){
    _name = name;
    _cost = cost;
    _quantity = NULL;
};

Product::~Product(){};

void Product::set_quantity(int quantity){
    if (quantity < 0){
        std::cerr << "Cannot have negative quantity";
    }
    _quantity = quantity;
};

double Product::price() const {
    return 2;
};

std::ostream & operator<< (std::ostream &ost, const Product &product){
    if (product._quantity > 0)
        ost << product._name << " (" << product._quantity << " @ " << std::fixed << std::setprecision(2) << std::setfill('0') << product.price() << ")";
    else
        ost << product._name << " (" << std::fixed << std::setprecision(2) << std::setfill('0') << product.price() << ")";

    return ost;
};

double Taxed::_tax = 0.0825;

Taxed::~Taxed(){};

void Taxed::set_tax_rate(double sales_tax) {
    Taxed::_tax = sales_tax;
};

double Taxed::price() const{
    return _quantity * _cost * (1+_tax);
}


Taxfree::~Taxfree(){};

double Taxfree::price() const{
    return _quantity * _cost;
}
Libra
  • 2,544
  • 1
  • 8
  • 24
  • How are you trying to call `price`? Could you provide a complete minimal, reproducible example? – N. Shead Feb 25 '20 at 04:58
  • @N.Shead I added the main method, however price is being called within the << operator overload – Libra Feb 25 '20 at 04:59

1 Answers1

9

You are experiencing object slicing. By storing a std::vector<Product>, you are actually creating instances of the base Product class and losing your instances of Taxed and Taxfree. In products.push_back(Taxfree("Apple", 2)), the Taxfree is passed to the compiler-generated copy constructor Product(const Product&), because a Taxfree object can bind to a const Product&.

Had you removed the base implementation of price() and made it a pure virtual function with virtual double price() const = 0;, you would have noticed this problem when your program failed to compile (because Product would become an abstract class and constructing it would no longer be possible).

Instead, you will need to use something like std::vector<std::shared_ptr<Product>>:

std::vector<std::shared_ptr<Product>> products;
products.push_back(std::make_shared<Taxfree>("Apple", 2));
products.push_back(std::make_shared<Taxed>("Iron",1.75));
products.push_back(std::make_shared<Taxfree>("Soda",2.5));
products.push_back(std::make_shared<Taxfree>("Lemon",2.5));

(I'd suggest unique_ptr instead, but it looks like you want the products and cart to contain the same object. Or, you could use unique_ptr and just create copies of the products, which is probably better if you plan to mutate them.)

jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • Could I use a vector of pointers? i.e. `std::vector products` – Libra Feb 25 '20 at 14:21
  • Yes, but then you will need to call new/delete yourself and it’s famously hard to do it correctly. I highly recommend using smart pointers such as unique_ptr or shared_ptr. – jtbandes Feb 25 '20 at 15:34