Retrieving and referencing names stored in a vector

Over the past couple months now I have been working on independent small projects and working my way through an old textbook from 90's. I've had some great advice from this community so far! I have been stuck on my most recent for a little over a week now would like to find some guidance as to complete this.

Reading through the guidelines of this forum I noticed that this may seem like a Homework assignment for a class or something, but I can assure you that I'm not a student. This is purely for me to learn, strengthen my knowledge, and skills in C++. I'm an EM physicist for a software company and want to eventual work my way into the software side of the company as well.

The Question:

So I have been trying to build a general program to:
1.) create a list of pets
2.) store the list of pets
3.) interact with the pets with them responding with their language( dog "woof", cat "meow")

Ultimately It's a misunderstanding of pointers.

I have built the initial part of the program and the logic structured for the second. My troubles come from cross referencing names input into the vector and using their speak function when spoken to.

Main implementation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "stdafx.h"

#include <iostream>
#include <vector>
#include "Dog.h"
#include "Cat.h"

int main()
{
	std::vector< Pet* > listOfPets;
	std::string userCommand;

	std::cout << "Enter a list of pet type and pet name in the form <Dog|Cat Name>. When you are finished enter <done>. \n";
	while (userCommand != "done")
	{
		std::getline(std::cin, userCommand);

		if (userCommand != "done")
		{
			std::string petType, petName;

			int indexOfSpace = userCommand.find_first_of(' ');
			petType = userCommand.substr(0, indexOfSpace);
			petName = userCommand.substr(indexOfSpace + 1);

			if (petType == "Dog" || petType == "dog")
			{
				listOfPets.push_back(new Dog(petName));
			}
			else if (petType == "Cat" || petType == "cat")
			{
				listOfPets.push_back(new Cat(petName));
			}
			else
			{
				std::cout << "I don't understand.\n";
				std::cout << "Enter the pet type and the pets name.\n";
			}
		}

	}

	//Here we will allow you to speak to the pets

	std::string human;

	std::cout << "\nSay hello to a specific pet by addressing its name. 'Hello <pet name>' \n when you are done say 'Goodbye'\n";

	while (human != "Goodbye")
	{
		std::getline(std::cin, human);

		if (human != "Goodbye")
		{
			std::string petName, hello;

			int indexOfSpace = human.find_first_of(' ');
			hello = human.substr(0, indexOfSpace);
			petName = human.substr(indexOfSpace + 1);

			if (hello == "Hello" || hello == "hello")
			{ 
		//I Believe this is where I want to create the pointers to the pets. explicitly how would I do that 

				if (petName == "Everybody")  
				{
					//All pets in the list will speak 

					std::cout << "This is just a test to see if i get here \n";

				}
				else if (petName ==  ) //HERE IS also another place where I am stuck. how to check if the petName is in the listOfPets vector which is associated to a Dog. 
				{
					//Dogs will speak 
				

					std::cout << petName << /*dog->speak();*/ "\n";

					
				}
				//                 else if ( petName ==  )
				//                 {
				//                     //Cats will speak 
				// 
				//                     speak();
				//                 }
				else
				{
					std::cout << "That name is not on the list. ";
				}

			}
			else
			{
				std::cout << "You need to say Hello <pet name> or Goodbye\n";
			}

		}
	}

}


Reading through the guidelines of this forum I noticed that this may seem like a Homework assignment for a class or something, but I can assure you that I'm not a student. This is purely for me to learn, strengthen my knowledge, and skills in C++. I'm an EM physicist for a software company and want to eventual work my way into the software side of the company as well.


Pets.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef Pet_header_included
#define Pet_header_included

#include <string>
#include <iostream>


class Pet
{
public:
	Pet(const std::string &m_petName);

	virtual ~Pet(void);

	//Pure virtual for animal sound
	virtual void speak(void) const = 0;

	std::string m_petName;

};

#endif //Pet_header_included 


Pet.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "stdafx.h"
#include "PETS.h"
#include <string>
#include <iostream>

Pet::Pet(const std::string &m_petName)
	: m_petName(m_petName)
{
	return;
}

Pet::~Pet()
{
	std::cout << "Goodbye from the Pet destructor " << std::endl;
	return;
}


Dog.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef Dog_header_included
#define Dog_header_included

#include "PETS.h"

//Dog class inherits from Pet 
class Dog : public Pet
{
public:
	Dog(const std::string &petName);

	//reason why this is virtual as well just in case we wanted to add another subclass such as "pug or pitbull" 
	virtual void speak(void) const;


	virtual ~Dog();

};


#endif //Dog_header_included 


Dog.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "stdafx.h"

#include "Dog.h"
#include <iostream>

Dog::Dog(std::string const &petName)
	: Pet(petName)
{
	return;
}

void Dog::speak() const
{
	std::cout << "Woof!" << std::endl;

	return;
}


Dog::~Dog()
{
	std::cout << "Goodbye from Dog destructor " << std::endl;

	return;
}


I also have a Cat class that is identical to Dog, just with meow instead.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>
#include <string>
#include <cctype>
#include <vector>

struct pet
{
    explicit pet( std::string its_name ) : name(its_name) {}

    virtual ~pet() = default ;
    void speak() const { std::cout << name << ": " << sound() << "!\n" ; }
    const std::string& its_name() const { return name ; }
    std::string its_type() const { return type() ; }

    protected:
        virtual std::string sound() const = 0 ;
        virtual std::string type() const = 0 ;
        const std::string name ;
};

struct dog : pet
{
    using pet::pet ;
    protected:
        virtual std::string sound() const override { return "woof" ; }
        virtual std::string type() const override { return "dog" ; }
};

struct cat : pet
{
    using pet::pet ;
    protected:
        std::string sound() const override { return "meow" ; }
        virtual std::string type() const override { return "cat" ; }
};

std::string& make_lower( std::string& str )
{
    for( char& c : str ) c = std::tolower( (unsigned char)c ) ;
    return str ;
}

// note: strongly consider using std::unique_ptr instead of raw pointers
std::vector<pet*> make_pets()
{
    std::cout << "Enter a list of pet type and pet name in the form <Dog|Cat Name>.\n"
              << "When you are finished enter <done>.\n";

    std::vector<pet*> all_pets ;

    std::string type;
    std::string name ;
    while( std::cout << "Enter pet type and pet name (eg. Dog Fido or Cat Felix): " &&
           std::cin >> type && make_lower(type) != "done" &&
           std::cin >> name )
    {
        if( type == "cat" ) all_pets.push_back( new cat(name) ) ;
        else if( type == "dog" ) all_pets.push_back( new dog(name) ) ;
        else std::cout << "unsupported pet type\n" ;
    }

    return all_pets ;
}

// say hello to a specific pet with a matching name
void say_hello( const std::vector<pet*>& pets, std::string pet_name )
{
    // range based loop: http://www.stroustrup.com/C++11FAQ.html#for
    for( const pet* ptr : pets )
    {
        std::string its_name = ptr->its_name() ;
        if( make_lower(its_name) == make_lower(pet_name) ) return ptr->speak() ;
    }

    std::cout << "did not find a pet with name '" << pet_name << "'\n" ;
}

void speak_with_pets( const std::vector<pet*>& pets )
{
    std::cout << "\nSay hello to a specific pet by addressing its name. 'Hello <pet name>' \n"
              << "when you are done say 'Goodbye'\n";

    std::string greeting ;
    std::string name ;
    while( std::cout << "Enter greeting (eg. Hello Fido or Hello Everybody): " &&
           std::cin >> greeting && make_lower(greeting) != "goodbye" &&
           std::cin >> name )
   {
       if( greeting != "hello" ) std::cout << "invalid input\n" ;

       else if( make_lower(name) == "everybody" ) // say hello to all
           for( const pet* ptr : pets ) ptr->speak() ;

       else say_hello( pets, name ) ; // say hello to a specific pet
   }
}

int main()
{
    const auto all_pets = make_pets() ;

    std::cout << "the pets are:\n--------\n" ;
    for( const auto& ptr : all_pets )
        std::cout << ptr->its_name() << " (" << ptr->its_type() << ")\n" ;

    speak_with_pets(all_pets) ;

    for( pet* ptr : all_pets ) delete ptr ;
}
Thank you, I do appreciate the help.

And the style is tight and clean.
Much different from what I'm used to reading in these text books.
Interpreting what some of these shorthand ways of doing things is a great tool to be exposed to.

Do most professional C++ programmers utilize this style?

Also yes I understand this is a small task versus large scale projects, but what is the advantage over the other using struct and class like I was doing?

In this argument as well, lets say two or more pets were given the same name, and would like both of them to respond?
The way that I read it is that the condition sequentially goes down the list of pets input and when it gets to that pet name, it stops searching then returns the name?



> Do most professional C++ programmers utilize this style?

As far as the details go (placing of braces, spaces, const int& ref vs. int const &ref etc.), there are a wide variety of different styles in vogue.

One thing that is certain is that a professional programmer would tend to avoid raw pointers and explicit calls to new and delete. And might code a little more defensively (check for null pointer etc.).


> what is the advantage over the other using struct and class like I was doing?

This toy example too uses classes like you were doing; the only difference is that for reasons of brevity, the classes were not partitioned into their own components (header and cpp files).


> lets say two or more pets were given the same name, and would like both of them to respond?

No. In the code posted above, the function returns immediately once a pet with the name has spoken. To get all the pets with that name to speak, we would just continue iterating till the end.

Using smart pointers instead of smart pointers, and getting all the pets with the name to speak, the code would be something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
#include <memory>

struct pet
{
    explicit pet( std::string its_name ) : name(its_name) {}

    virtual ~pet() = default ;
    void speak() const { std::cout << name << ": " << sound() << "!\n" ; }
    const std::string& its_name() const { return name ; }
    std::string its_type() const { return type() ; }

    // https://en.cppreference.com/w/cpp/memory/unique_ptr
    // http://www.drdobbs.com/cpp/c11-uniqueptr/240002708
    // https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-newdelete
    using pointer = std::unique_ptr<pet> ;

    protected:
        virtual std::string sound() const = 0 ;
        virtual std::string type() const = 0 ;
        const std::string name ;
};

struct dog : pet
{
    using pet::pet ;
    protected:
        virtual std::string sound() const override { return "woof" ; }
        virtual std::string type() const override { return "dog" ; }
};

struct cat : pet
{
    using pet::pet ;
    protected:
        std::string sound() const override { return "meow" ; }
        virtual std::string type() const override { return "cat" ; }
};

std::string& make_lower( std::string& str )
{
    for( char& c : str ) c = std::tolower( (unsigned char)c ) ;
    return str ;
}

std::vector<pet::pointer> make_pets()
{
    std::cout << "Enter a list of pet type and pet name in the form <Dog|Cat Name>.\n"
              << "When you are finished enter <done>.\n";

    std::vector<pet::pointer> all_pets ;

    std::string type;
    std::string name ;
    while( std::cout << "Enter pet type and pet name (eg. Dog Fido or Cat Felix): " &&
           std::cin >> type && make_lower(type) != "done" &&
           std::cin >> name )
    {
        // https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
        if( type == "cat" ) all_pets.push_back( std::make_unique<cat>(name) ) ;
        else if( type == "dog" ) all_pets.push_back( std::make_unique<dog>(name) ) ;
        else std::cout << "unsupported pet type\n" ;
    }

    return all_pets ;
}

// say hello to all pets with matching names
void say_hello( const std::vector<pet::pointer>& pets, std::string pet_name )
{
    bool matched = false ;

    // range based loop: http://www.stroustrup.com/C++11FAQ.html#for
    for( const pet::pointer& ptr : pets )
    {
        if( ptr != nullptr )
        {
            std::string its_name = ptr->its_name() ;
            // if( make_lower(its_name) == make_lower(pet_name) ) return ptr->speak() ;
            if( make_lower(its_name) == make_lower(pet_name) )
            {
                ptr->speak() ; // don't return immediately, continue searching for more pets
                matched = true ;
            }
        }
    }

    if( !matched ) std::cout << "did not find any pet with name '" << pet_name << "'\n" ;
}

void speak_with_pets( const std::vector<pet::pointer>& pets )
{
    std::cout << "\nSay hello to a specific pet by addressing its name. 'Hello <pet name>' \n"
              << "when you are done say 'Goodbye'\n";

    std::string greeting ;
    std::string name ;
    while( std::cout << "Enter greeting (eg. Hello Fido or Hello Everybody): " &&
           std::cin >> greeting && make_lower(greeting) != "goodbye" &&
           std::cin >> name )
   {
       if( greeting != "hello" ) std::cout << "invalid input\n" ;

       else if( make_lower(name) == "everybody" ) // say hello to all
       {
           for( const pet::pointer& ptr : pets ) if( ptr != nullptr ) ptr->speak() ;
       }

       else say_hello( pets, name ) ; // say hello to pets with this name
   }
}

int main()
{
    const auto all_pets = make_pets() ;

    std::cout << "the pets are:\n--------\n" ;
    for( const auto& ptr : all_pets )
        std::cout << ptr->its_name() << " (" << ptr->its_type() << ")\n" ;

    speak_with_pets(all_pets) ;
}
Once again, thank you.

Yeah that makes complete sense. Thank you for the links to information as well!!

Since I've been integrating into the software side of things, I've been pairing up with different colleagues learning a little each day, pure exposure to the code. Learn how every single one of them has their own way of thinking in terms of logic implementation. How just like in any field there are multiple directions to take reaching the same outcome. And because of that documentation is highly important (which much of our older code is lacking because it was initially a small core group of guys who all had their hands on it).

Also coincidentally just this morning my colleague was talking with me about how defensively our code framework is and about general standards throughout. Some universally accepted, others company specific depending.

I will definitely be back in the near future with more learning questions, and more textbook derived samples that I will seek guidance with. As always thank you for your help and your time. Everyone has to start somewhere
Topic archived. No new replies allowed.