Thread: How do you overload class constructors?

  1. #1
    Registered User
    Join Date
    Jul 2019
    Posts
    34

    How do you overload class constructors?

    I'm re-learning how to create classes and use inheritance from this one site, LearnCPP.com, I am trying to figure something out in my book class. I want to overload the Book constructor to only hold text so I can enter in really long strings of text. I'll fix this later by using a vector to hold the strings and each element be a page, but for now I'm wondering if overloading inherited constructors can be done?

    Another question, This is a basic inheritance example I made, taking from the LearnCPP site, adding my own stuff and classes to it, hopefully I did everything right, I'm just wondering as it is right now, is it constructed well, following good programming form?

    Last question. If I made a Weapon class for swords, it would inherit from item, since swords are items, but sword would inherit from weapons and not items so how would that look??? It has to inherit from weapon but it's still an item so im a bit confused as to how its going to be a weapon AND an item at the same time without having to use multiple inheritance, which is what i want to avoid.

    Code:
    #include <iostream>#include <vector>
    
    
    class Item
    {
    public:
    	Item(const std::string& name, float cost, float value, float weight) 
    		: name(name), cost(cost), value(value), weight(weight) {}
    
    
    	std::string DisplayName() const { return name; }
    	float DisplayCost() const { return cost; }
    	float DisplayValue() const { return value; }
    	float DisplayWeight() const { return weight; }
    
    
    private:
    	std::string name{ "Generic Item" };
    	float cost{ 2 };
    	float value{ 1 };
    	float weight{ 1 };
    };
    
    
    class Book : public Item
    {
    	public:
    		Book(const std::string& name, float cost, float value, float weight, const std::string& text, const std::string& title, const std::string& author)
    			: Item{ name, cost, value, weight }, text(text), title(title), author(author){}
    
    
    		//Book(const std::string& text): text(text){} //How do you do this??
    
    
    		std::string Read() const { return text; }
    		std::string DisplayTitle() const { return title; }
    		std::string DisplayAuthor() const { return author; }
    
    
    	private:
    		std::string text{ "Default" };
    		std::string title{ "Title" };
    		std::string author{ "Author Name" };
    };
    
    
    class Sword : public Item
    {
    public:
    	Sword(const std::string& name, float cost, float value, float weight, float attackPower, float durability) 
    		: Item{ name, cost, value, weight }, attackPower(attackPower), durability(durability){}
    
    
    	float DisplayAttackPower() const { return attackPower; }
    	float DisplayDurability() const { return durability; }
    
    
    	friend std::ostream& operator<<(std::ostream& os, Sword& sword)
    	{
    		os << "Name: " << sword.DisplayName() << std::endl;
    		os << "Cost: " << sword.DisplayCost() << std::endl;
    		os << "Value: " << sword.DisplayValue() << std::endl;
    		os << "Weight: " << sword.DisplayWeight() << std::endl;
    		os << "Attack Power: " << sword.DisplayAttackPower() << std::endl;
    		os << "Durability: " << sword.DisplayDurability() << std::endl;
    
    
    		return os;
    	}
    
    
    private:
    	float attackPower{ 5 };
    	float durability{ 1 };
    };
    
    
    int main()
    {
    	Sword Greatsword{"Greatsword", 1000, 800, 15, 34, 100};
    	Sword BastardSword{"Bastard Sword", 1500, 1200, 20, 50, 100};
    
    
    	std::cout << Greatsword << std::endl;
    	std::cout << BastardSword << std::endl;
    
    
    	Book AtlasOfDragons
    	(
    		"Book", 
    		50, 
    		30, 
    		3, 
    		"Herein is recorded the list of known dragons, both living and dead, including those slain by the Dragonguard since the time of its founding, as well as those slain in earlier ages, where they can be identified. Unfortunately, only a few of the dragons slain by our Akaviri predecessors during the Crusade were recorded and thus this list is sadly incomplete.", 
    		"Atlas of Dragons", 
    		"Brother Mathnan"
    	);
    
    
    	std::cout << AtlasOfDragons.DisplayTitle() << std::endl;
    	std::cout << "By " << AtlasOfDragons.DisplayAuthor() << '\n' << std::endl;
    	std::cout << AtlasOfDragons.Read() << std::endl;
    
    
    	return 0;
    }

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    How about
    Code:
    #include <iostream>
    #include <string>
    
    class Item
    {
    public:
      Item(const std::string& name, float cost, float value, float weight)
        : name(name), cost(cost), value(value), weight(weight) {}
    
    
      std::string DisplayName() const { return name; }
      float DisplayCost() const { return cost; }
      float DisplayValue() const { return value; }
      float DisplayWeight() const { return weight; }
    
      friend std::ostream& operator<<(std::ostream& os, const Item& item)
      {
        os << "Name: " << item.name << std::endl;
        os << "Cost: " << item.cost << std::endl;
        os << "Value: " << item.value << std::endl;
        os << "Weight: " << item.weight << std::endl;
      }
    
    protected:  //!! can be accessed directly by derived classes
      std::string name{ "Generic Item" };
      float cost{ 2 };
      float value{ 1 };
      float weight{ 1 };
    };
    
    class Weapon: public Item {
    public:
      Weapon(const std::string& name, float cost, float value, float weight)
        : Item{ name, cost, value, weight }
        {}
      friend std::ostream& operator<<(std::ostream& os, const Weapon& weapon)
      {
        os << static_cast<const Item &>(weapon);
        return os;
      }
    };
    
    class Sword : public Weapon
    {
    public:
      Sword(const std::string& name, float cost, float value, float weight, float attackPower, float durability)
        : Weapon{ name, cost, value, weight },
          attackPower(attackPower),
          durability(durability)
          {}
    
      friend std::ostream& operator<<(std::ostream& os, const Sword& sword)
      {
        os << static_cast<const Weapon &>(sword);
        os << "Attack Power: " << sword.attackPower << std::endl;
        os << "Durability: " << sword.durability << std::endl;
        return os;
      }
    
      float DisplayAttackPower() const { return attackPower; }
      float DisplayDurability() const { return durability; }
    
    private:
      float attackPower{ 5 };
      float durability{ 1 };
    };
    
    int main ( ) {
      Sword Greatsword{"Greatsword", 1000, 800, 15, 34, 100};
      std::cout << Greatsword << std::endl;
    }
    
    
    $ g++ -std=c++11 foo.cpp
    $ ./a.out 
    Name: Greatsword
    Cost: 1000
    Value: 800
    Weight: 15
    Attack Power: 34
    Durability: 100
    The Weapon class itself (as can any class) have no members, but it does serve to preserve a relationship.
    Or you could add 'damage' to the Weapon class, if all weapons can do damage.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by ChayHawk
    I want to overload the Book constructor to only hold text so I can enter in really long strings of text. I'll fix this later by using a vector to hold the strings and each element be a page, but for now I'm wondering if overloading inherited constructors can be done?
    You can certainly overload the Book constructor. Constructors aren't exactly inherited. Consider this example:
    Code:
    #include <iostream>
    
    class X
    {
    public:
        explicit X(int n_) : n(n_) {}
    
        int get() const
        {
            return n;
        }
    private:
        int n;
    };
    
    class Y : public X {};
    
    int main()
    {
        Y y{123};
        std::cout << y.get() << std::endl;
    }
    If constructors were inherited, then we would expect this program to compile and print "123". In fact, it does not compile, because there is no such constructor declared for Y. We have to define it:
    Code:
    class Y : public X
    {
    public:
        explicit Y(int n) : X(n) {}
    };
    but now you can see that Y can invoke the non-private constructor of X to do the job of initialising the X subobject of the Y object.

    Quote Originally Posted by ChayHawk
    This is a basic inheritance example I made, taking from the LearnCPP site, adding my own stuff and classes to it, hopefully I did everything right, I'm just wondering as it is right now, is it constructed well, following good programming form?
    Yes and no. Yes, in the sense that at your stage in learning C++, this is probably okay. You're doing well. No, in the sense that what you're doing is inheriting publicly so as to inherit implementation only rather than interface, and this could mean that you might want to rethink the design to inherit the interface, or switch to composition, or use private inheritance instead.

    If you want to stick to public inheritence, one common thing that this entails is declaring the base class destructor to be virtual so that if you have a base class pointer to a derived class object, you can destroy the derived class object through the base class pointer without having to determine the derived class type of the object.

    Quote Originally Posted by ChayHawk
    Last question. If I made a Weapon class for swords, it would inherit from item, since swords are items, but sword would inherit from weapons and not items so how would that look??? It has to inherit from weapon but it's still an item so im a bit confused as to how its going to be a weapon AND an item at the same time without having to use multiple inheritance, which is what i want to avoid.
    You don't have to use multiple inheritance because Weapon inherits from Item, and Sword inherits from Weapon, therefore Sword is a Weapon and Sword is an Item. But this is related to the notion of using public inheritance to inherit interface. There is the Liskov substitution principle, which is the idea that anywhere you can use a base class object, you can correctly substitute it with one of its derived class objects, i.e., we can see the base class as a supertype and the derived classes as subtypes.

    Incidentally, if you're returning a value rather than displaying it by say, printing it to standard output, I'd suggest using the verb "Get" rather than "Display".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    @Salem, thank you!

    Quote Originally Posted by laserlight View Post
    Yes and no. Yes, in the sense that at your stage in learning C++, this is probably okay. You're doing well. No, in the sense that what you're doing is inheriting publicly so as to inherit implementation only rather than interface, and this could mean that you might want to rethink the design to inherit the interface, or switch to composition, or use private inheritance instead.

    If you want to stick to public inheritance, one common thing that this entails is declaring the base class destructor to be virtual so that if you have a base class pointer to a derived class object, you can destroy the derived class object through the base class pointer without having to determine the derived class type of the object.


    You don't have to use multiple inheritance because Weapon inherits from Item, and Sword inherits from Weapon, therefore Sword is a Weapon and Sword is an Item. But this is related to the notion of using public inheritance to inherit interface. There is the Liskov substitution principle, which is the idea that anywhere you can use a base class object, you can correctly substitute it with one of its derived class objects, i.e., we can see the base class as a supertype and the derived classes as subtypes.

    Incidentally, if you're returning a value rather than displaying it by say, printing it to standard output, I'd suggest using the verb "Get" rather than "Display".
    Ok, yeah I haven't gotten to virtual yet in the course, that's in the next chapter, but I wanted to make sure I'm doing basic inheritance correctly first before moving on, I want to get a solid foundation then go to inheritance, then onward from there, that way I'm not learning too much and then forgetting it all.

    There is a composition chapter, I'll have to go over that. Could I just name my getters something like AttackPower and Durability? I think that putting Get in front of everything is kind of ugly and redundant even though it adds more clarity as to what its doing, but idk that's just me, I would certainly find a way to name it in a way that details what it does (get or set the variable).

    Also what is the difference of inheriting interface as opposed to functionality? lets say im making a game out of this, why would that be important for that type of project?

    And with the inheritance access, why make it private instead of public? I looked up some topics on that and i'm still confused.

    Code:
    #include <iostream>#include <string>
    
    
    class Item
    {
        public:
            Item(const std::string& name, float cost, float value, float weight)
                : name(name), cost(cost), value(value), weight(weight)
            {}
    
    
    
    
            std::string Name() const { return name; }
            float Cost() const { return cost; }
            float Value() const { return value; }
            float Weight() const { return weight; }
    
    
            friend std::ostream& operator<<(std::ostream& os, const Item& item)
            {
                os << "Name: " << item.name << std::endl;
                os << "Cost: " << item.cost << std::endl;
                os << "Value: " << item.value << std::endl;
                os << "Weight: " << item.weight << std::endl;
    
    
                return os;
            }
    
    
        protected:  //!! can be accessed directly by derived classes
            std::string name{ "Generic Item" };
            float cost{ 2 };
            float value{ 1 };
            float weight{ 1 };
    };
    
    
    class Weapon : public Item 
    {
        public:
            Weapon(const std::string& name, float cost, float value, float weight, float attackPower, float durability)
                : Item{ name, cost, value, weight }, attackPower(attackPower), durability(durability)
            {}
    
    
            friend std::ostream& operator<<(std::ostream& os, const Weapon& weapon)
            {
                os << static_cast<const Item&>(weapon);
                os << "Attack Power: " << weapon.attackPower << std::endl;
                os << "Durability: " << weapon.durability << std::endl;
    
    
                return os;
            }
    
    
            float AttackPower() const { return attackPower; }
            float Durability() const { return durability; }
    
    
        private:
            float attackPower{ 5 };
            float durability{ 1 };
    };
    
    
    class Sword : public Weapon
    {
        public:
            Sword(const std::string& name, float cost, float value, float weight, float attackPower, float durability)
                : Weapon{ name, cost, value, weight, attackPower, durability }
            {}
    };
    
    
    class Bow : public Weapon
    {
        public:
            Bow(const std::string& name, float cost, float value, float weight, float attackPower, float durability, float range)
                : Weapon{ name, cost, value, weight, attackPower, durability }, range(range)
            {}
    
    
            float Range() const { return range; }
    
    
            friend std::ostream& operator<<(std::ostream& os, const Bow& bow)
            {
                os << static_cast<const Weapon&>(bow);
                os << "Range: " << bow.range << std::endl;
    
    
                return os;
            }
    
    
        private:
            float range{ 1 };
    };
    
    
    int main() {
        Sword Greatsword{ "Greatsword", 1000, 800, 15, 34, 100 };
        Sword BastardSword{ "Bastard Sword", 1300, 1000, 25, 50, 100 };
    
    
        std::cout << Greatsword << std::endl;
        std::cout << BastardSword << std::endl;
    
    
        Bow Greatbow{ "Great Bow", 600, 300, 4, 60, 100, 600};
    
    
        std::cout << Greatbow << std::endl;
    }

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by ChayHawk
    There is a composition chapter, I'll have to go over that. Could I just name my getters something like AttackPower and Durability? I think that putting Get in front of everything is kind of ugly and redundant even though it adds more clarity as to what its doing, but idk that's just me, I would certainly find a way to name it in a way that details what it does (get or set the variable).
    That's up to you, though I would suggest aiming for some consistency. Some people like sticking to the "verb + name" convention of naming functions, other people think it is unnecessary. My point was that if you want to use a verb, "Display" doesn't sound right for a function that returns rather than displays.

    Quote Originally Posted by ChayHawk
    Also what is the difference of inheriting interface as opposed to functionality? lets say im making a game out of this, why would that be important for that type of project?
    When you design to inherit interface, you're saying that this base class is a supertype that can be used in various client code. You can then derive from the base class subtypes that implement the same interface to do their own thing. The existing client code can effectively gain the new functionality provided by these subtypes without having to change, i.e., you're looking at the use of inheritance for polymorphism, rather than inheritance as a convenient way to avoid re-implementing member functions.

    After you have gone through learncpp's chapter on virtual functions, I recommend reading these articles on four core OO principles:

    Keep in mind that these are more guiding principles for OO design than strict rules you must follow. As others have observed, one problem with OO design seems to be that you could end up over-engineering* unless you have a crystal ball to determine when to apply OO principles.

    * which then breaks another principle, but this time from extreme programming: You Aren't Gonna Need It (YAGNI), i.e., to avoid implementing things that you don't need yet just because you anticipate that you'll need them at some point in the future.

    Quote Originally Posted by ChayHawk
    And with the inheritance access, why make it private instead of public? I looked up some topics on that and i'm still confused.
    You could do that if you specifically wanted to inherit functionality only, e.g., you want the convenience of being able to inherit the member functions from the base class with a little work (as in using declarations rather than using composition where you would have to forward the functions), but you don't want to establish an is-a relationship between the base class and the derived class.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    Thank you for the explanation, I'll look at those links. I'm still looking through the inheritance chapter and re reading it to make sure I really understand it.

    I was working on my code, and I noticed something with the items. When adding them to a vector, I cannot replace them. Lets say I use an item up, and i want to remove it from the inventory, how would I go about doing that? I created a small program just to test this to make sure nothing else was interfering in my other program. I did a little research on it but couldn't find too much that I understood.


    Visual Studio gives me one error message, which is:

    Error C2676 binary '==': 'Item' does not define this operator or a conversion to a type acceptable to the predefined operator

    How can I fix this?

    Here is that program:

    Code:
    #include <iostream>#include <string>
    #include <vector>
    #include <algorithm>
    
    
    using std::cout;
    using std::endl;
    using std::string;
    using std::vector;
    
    
    class Item
    {
        public:
            Item(const string& name): m_name(name)
            {}
    
    
            friend std::ostream& operator<<(std::ostream& os, const Item& item)
            {
                os << item.m_name;
    
    
                return os;
            }
    
    
        private:
            string m_name{ "Item" };
    };
    
    
    int main()
    {
        Item Gold("Gold");
    
    
        vector<Item> inventory{};
        inventory.push_back(Gold);
    
    
        for(auto& i : inventory)
        {
            cout << i;
        }
    
    
        for (auto& i : inventory)
        {
            //Error, cannot do this
            inventory.erase(remove(inventory.begin(), inventory.end(), Gold), inventory.end());
            cout << i;
        }
    
    }

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Looking at the Learncpp syllabus, you would have covered operator overloading in chapter 13. You need to do that here because the std::remove generic algorithm expects that the type of the objects being compared has operator== overloaded so that it can find the object to remove.

    I am wary about calling the inventory.erase in a range-based for loop in which you loop over inventory's items. My concern is that erase could (will?) invalidate iterators after the point of erasure (and maybe more, I don't remember the details offhand), so the range-based for loop might result in undefined behaviour if indeed this is the case. Why do you want to loop over inventory while erasing from it though?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    I'll have to re look at that section, I've been skipping around a little here and there depending on my needs, but as for the ranged based for loop, it was just something quick to try. In my other code i was experimenting with deleting the element that held the class object but i couldn't figure out how to get the element where the item i was looking for was at, since items can be inserted in any order, like if the player picks up different items in any order they want then I'm unsure how to find that element.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by ChayHawk
    In my other code i was experimenting with deleting the element that held the class object but i couldn't figure out how to get the element where the item i was looking for was at, since items can be inserted in any order, like if the player picks up different items in any order they want then I'm unsure how to find that element.
    You appear to have solved that by using the remove + erase idiom.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    So this:

    Code:
     for(auto& i : inventory)    {
            //Error, cannot do this
            inventory.erase(remove(inventory.begin(), inventory.end(), Gold), inventory.end());
            cout << i;
        }
    Does work, I just need to overload it with this:

    Code:
    friend std::ostream& operator<<(std::ostream& os, const Item& item)        {
                os << item.m_name;
    
                return os;
            }
    But modify it to use the == operator instead of the << operator?

  11. #11
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    I got it working. After searching for a while I found someone with the same issue as me, so I copied in an answers code and played with it until I got it working.

    Now I just need to figure out how to get the ID of the item, or create one for it and store it with it.

    Code:
    #include <iostream>
    #include <string>
    #include <vector>
    
    class Item
    {
    	public:
    	    Item(const std::string& name): name(name)
    	    {}
    	    
    	      friend std::ostream& operator<<(std::ostream& os, Item& item)
    	    {
    	    	os << "Name: " << item.name;
    	    	
    	    	return os;
    	    }
    	
            std::string name;
            int id;
    };
    
    bool operator==(const Item& l, const Item& r) {
        return l.id   == r.id
        &&     l.name == r.name;
    }
    
    int main()
    {
    	Item Gold("Gold");
    	Item Book("Book");
    	Item Potion("Potion");
    	
    	std::vector<Item> inv;
    	inv.push_back(Gold);
    	inv.push_back(Book);
    	inv.push_back(Potion);
    	
    	std::cout << "Current Inventory" << std::endl;
    	
    	for(auto& i : inv)
    	{
    		std::cout << i << std::endl;
    	}
    	std::cout << inv.size() << std::endl;
    	
    	inv.erase(std::remove(inv.begin(), inv.end(), Gold), inv.end());
    	
       for(auto& i : inv)
    	{
    		std::cout << i << std::endl;
    	}
    	std::cout << inv.size() << std::endl;	
    }

  12. #12
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    Quick question, how would I remove the elements from a vector of pairs or tuples?

    I was trying to figure it out but i dont know if its doable with a pair or tuple, I dont have that code to show since i did it on my phone while i was at work.

    Updated Code:

    Code:
    #include <iostream>#include <string>
    #include <vector>
    
    
    class Item
    {
    public:
    	Item(const std::string& name) : name(name)
    	{}
    
    
    	std::string GetName() const { return name; }
    
    
    	friend std::ostream& operator<<(std::ostream& os, Item& item)
    	{
    		os << "Name: " << item.name;
    
    
    		return os;
    	}
    
    
    private:
    	std::string name{ "Item" };
    	int amountOwned{ 0 };
    };
    
    
    bool operator==(const Item& l, const Item& r) {
    	return l.GetName() == r.GetName();
    }
    
    
    class Player
    {
    public:
    	Player(const std::string& name) : name(name)
    	{}
    
    
    	void AddItemToInventory(Item& itemName);
    
    
    	void OpenInventory();
    	void RemoveItemFromInventory(Item& item);
    
    
    
    
    private:
    	std::vector<Item> inventory{};
    	std::string name{ "player" };
    };
    
    
    void Player::AddItemToInventory(Item& item)
    {
    	inventory.push_back(item);
    }
    
    
    void Player::OpenInventory()
    {
    	for (auto& i : inventory)
    	{
    		std::cout << i << std::endl;
    	}
    }
    
    
    void Player::RemoveItemFromInventory(Item& item)
    {
    	inventory.erase(std::remove(inventory.begin(), inventory.end(), item), inventory.end());
    }
    
    
    int main()
    {
    	Item Gold("Gold");
    	Item Book("Book");
    	Item Potion("Potion");
    
    
    	Player Link("Link");
    
    
    	Link.AddItemToInventory(Gold);
    	Link.AddItemToInventory(Potion);
    	Link.OpenInventory();
    	Link.RemoveItemFromInventory(Gold);
    
    
    	Link.OpenInventory();
    
    
    }

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by ChayHawk
    Quick question, how would I remove the elements from a vector of pairs or tuples?

    I was trying to figure it out but i dont know if its doable with a pair or tuple, I dont have that code to show since i did it on my phone while i was at work.
    It is basically the same approach, e.g.,
    Code:
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <tuple>
    #include <vector>
    
    int main()
    {
        using namespace std;
    
        vector<tuple<string, string, int>> tuples;
        tuples.emplace_back("hello", "world", 123);
        tuples.emplace_back("foo", "bar", 456);
    
        for (const auto& item : tuples)
        {
            cout << get<0>(item) << ", " << get<1>(item) << ", " << get<2>(item) << "\n";
        }
        cout << "--------------------\n";
    
        tuples.erase(remove(tuples.begin(), tuples.end(), make_tuple("foo", "bar", 456)), tuples.end());
    
        for (const auto& item : tuples)
        {
            cout << get<0>(item) << ", " << get<1>(item) << ", " << get<2>(item) << "\n";
        }
    
        return 0;
    }
    Though you would need to be wary if floating point types are involved due to floating point inaccuracy.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    Registered User
    Join Date
    Jul 2019
    Posts
    34
    Awesome thank you. Now I have another issue, in my inventory where I am going to add stuff to the inventory, I need to search for the first item in the tuple, say im looking for a potion, if the potion item already exists, then it should increment the item amount counter, however because its a tuple, i cant figure out how to search JUST for the class object and not the value.

    My code doest seem to be entering the for loop for some reason, not sure why. I would think it would at least enter the for loop but it doesnt. Perhaps im making the inventory system more difficult than it needs to be idk, do you have any suggestions that would be easier or better?

    Code:
    void Player::AddItemToInventory(Item& item, const string& name, int amountToAdd){
        //Reaches this
        cout << "Inside AddItemToInventory function" << endl;
        for (auto& i : inventory)
        {
            //Not reaching this
            cout << "Inside For loop" << endl;
            if (get<0>(i) == item)
            {
                cout << "Inside if statement comparing get<0>(i) to item" << endl;
                cout << "Increment" << endl;
                amount += amountToAdd;
                cout << GetItemAmount() << endl;
            }
            else
            {
                //Not reaching this either
                cout << "inside else statement" << endl;
                cout << "Added " << item.GetName() << " to inventory." << endl;
                inventory.emplace_back(item, amountToAdd);
            }
        }
        //if (std::find(inventory.begin(), inventory.end(), std::make_tuple(item, amountToAdd)) != inventory.end() == true)
        //{
        //    //does not add to amount of items owned.
        //    std::cout << "Increment" << std::endl;
        //    amount += amountToAdd;
        //    std::cout << GetItemAmount() << std::endl;
        //}
        //else//if it does not exist in players inventory, then add it.
        //{
        ////this is being skipped because amountToAdd is not the same value, need to find a way to only search for Item name
        //std::cout << "Added " << item.GetName() << " to inventory." << std::endl;
        //inventory.emplace_back(item, amountToAdd);
        //}
    }

    Entire code:

    Code:
    #include <iostream>#include <string>
    #include <vector>
    #include <tuple>
    
    
    using std::cout;
    using std::endl;
    using std::vector;
    using std::string;
    using std::get;
    using std::tuple;
    using std::ostream;
    using std::remove;
    using std::make_tuple;
    
    
    class Item
    {
    public:
        Item(const string& name) : name(name)
        {}
    
    
        string GetName() const { return name; }
    
    
        friend ostream& operator<<(ostream& os, Item& item)
        {
            os << "Name: " << item.name;
    
    
            return os;
        }
    
    
    private:
        string name{ "Item" };
    };
    
    
    bool operator==(const Item& l, const Item& r)
    {
        return l.GetName() == r.GetName();
    }
    
    
    class Player
    {
    public:
        Player(const string& name) : name(name)
        {}
    
    
        void AddItemToInventory(Item& itemName, const string& name, int amount);
    
    
        void OpenInventory();
        void RemoveItemFromInventory(Item& item, int amount);
        int GetItemAmount() const { return amount; }
    
    
    private:
        vector<tuple<Item, int> > inventory{};
        string name{ "Player" };
        int amount{ 0 };
    };
    
    
    
    
    void Player::AddItemToInventory(Item& item, const string& name, int amountToAdd)
    {
        cout << "Inside AddItemToInventory function" << endl;
        for (auto& i : inventory)
        {
            //Not reaching this
            cout << "Inside For loop" << endl;
            if (get<0>(i) == item)
            {
                cout << "Inside if statement comparing get<0>(i) to item" << endl;
                cout << "Increment" << endl;
                amount += amountToAdd;
                cout << GetItemAmount() << endl;
            }
            else
            {
                //Not reaching this either
                cout << "inside else statement" << endl;
                cout << "Added " << item.GetName() << " to inventory." << endl;
                inventory.emplace_back(item, amountToAdd);
            }
        }
        //if (std::find(inventory.begin(), inventory.end(), std::make_tuple(item, amountToAdd)) != inventory.end() == true)
        //{
        //    //does not add to amount of items owned.
        //    std::cout << "Increment" << std::endl;
        //    amount += amountToAdd;
        //    std::cout << GetItemAmount() << std::endl;
        //}
        //else//if it does not exist in players inventory, then add it.
        //{
        ////this is being skipped because amountToAdd is not the same value, need to find a way to only search for Item name
        //std::cout << "Added " << item.GetName() << " to inventory." << std::endl;
        //inventory.emplace_back(item, amountToAdd);
        //}
    }
    
    
    void Player::OpenInventory()
    {
        for (auto& i : inventory)
        {
            cout << get<0>(i) << " x" << get<1>(i) << endl;
        }
    }
    
    
    void Player::RemoveItemFromInventory(Item& item, int amount)
    {
        inventory.erase(remove(inventory.begin(), inventory.end(), make_tuple(item, amount)), inventory.end());
    }
    
    
    int main()
    {
        Item Rupee("Rupee");
        Item Book("Book");
        Item Potion("Potion");
    
    
        Player Link("Link");
    
    
        Link.AddItemToInventory(Rupee, Rupee.GetName(), 5);
        Link.AddItemToInventory(Rupee, Rupee.GetName(), 7);
        Link.OpenInventory();
    }
    Last edited by ChayHawk; 02-05-2021 at 06:28 AM.

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I'd say that your commented out code has the right idea, but it needs to use find_if instead of find, e.g.,
    Code:
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <tuple>
    #include <vector>
    
    void add_item(std::vector<std::tuple<std::string, int>>& inventory, const std::string& item_name)
    {
        using std::get;
    
        auto iter = find_if(
            inventory.begin(),
            inventory.end(),
            [&item_name](const auto& item) { return get<0>(item) == item_name; }
        );
    
        if (iter != inventory.end())
        {
            ++get<1>(*iter);
        }
        else
        {
            inventory.emplace_back(item_name, 1);
        }
    }
    
    int main()
    {
        using namespace std;
    
        vector<tuple<string, int>> inventory;
    
        add_item(inventory, "apple");
        add_item(inventory, "banana");
        add_item(inventory, "apple");
    
        for (const auto& item : inventory)
        {
            cout << get<0>(item) << ": " << get<1>(item) << "\n";
        }
    }
    Having said that, a std::vector of tuples probably isn't the best way of representing your inventory. You seem to want to quickly access items by name, and each name maps to the number of items of that name, so a std::map or std::unordered_map may be better. For example:
    Code:
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <map>
    
    void add_item(std::map<std::string, int>& inventory, const std::string& item_name)
    {
        auto iter = inventory.find(item_name);
        if (iter != inventory.end())
        {
            ++iter->second;
        }
        else
        {
            inventory.emplace(item_name, 1);
        }
    }
    
    int main()
    {
        using namespace std;
    
        map<string, int> inventory;
    
        add_item(inventory, "apple");
        add_item(inventory, "banana");
        add_item(inventory, "apple");
    
        for (const auto& item : inventory)
        {
            cout << item.first << ": " << item.second << "\n";
        }
    }
    Of course, this won't work out of the box for you because you're using an Item type rather than std::string, but it means that you need to overload at least operator< for Item to be used as a key for the map.

    Also, Player::AddItemToInventory should have a const Item& parameter, without the name parameter since the item name can be obtained from the item.
    Last edited by laserlight; 02-05-2021 at 06:55 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting program and overload constructors to work
    By jlin55 in forum C++ Programming
    Replies: 5
    Last Post: 07-07-2017, 11:39 AM
  2. Overload conversion operator from outside class...
    By yaya in forum C++ Programming
    Replies: 4
    Last Post: 08-11-2010, 02:48 PM
  3. Overload == for string class
    By nickname_changed in forum C++ Programming
    Replies: 6
    Last Post: 10-02-2003, 05:07 PM
  4. Replies: 4
    Last Post: 12-29-2002, 12:29 AM
  5. class design and operation overload
    By Unregistered in forum C++ Programming
    Replies: 1
    Last Post: 12-03-2001, 10:49 PM

Tags for this Thread