Thread: Initializing array of class objects via loop

  1. #1
    Registered User
    Join Date
    Mar 2016
    Posts
    203

    Initializing array of class objects via loop

    Most of the book examples I've seen re initializing array of class objects have been 'by hand', e.g.

    Code:
     Dog dogs[2] = {Dog("Frisky", 2), Dog("Tabby", 3);
    where the data members are names and ages of the dogs for example, etc.

    I'm trying to initialize an array of similar class objects by looping the default constructor through the array but getting weird results. Though my array takes only 3 elements, the code is not reading the first 3 inputs but if I keep adding data it saves the 4th, 5th and 6th entries and prints them out. Any helps re where I'm going wrong would be much appreciated. Thanks

    Code:
    #include<iostream>using namespace std;
    
    
    const int MEMB = 3; 
    
    
    class Dog{
        private:
            string m_name;
            int m_age;
        public:
            Dog(){
                cout<<"Enter dog's name: ";
                string name;
                cin>>name;
                m_name = name;
                cout<<"Enter dog's age: ";
                int age;
                cin>>age;
                m_age = age; 
                
            }
            void showDog(){
                cout<<"The dog's name is: "<<m_name<<endl;
                cout<<"The dog's age is: "<<m_age<<" years"<<endl; 
            }
    };
    
    
    int main(){
        
        Dog list[MEMB];
        for (int i = 0; i < MEMB; i++){
            list[i]=Dog();
            cout<<endl; 
        }
        cout<<endl;
        for (int i = 0; i < MEMB; i++){
            list[i].showDog();
            cout<<endl; 
        }
     
    
    
    
    
    }

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Well you try to call your constructor 6 times, remember when you defined the array the default constructor is called. You really shouldn't be doing all that work in the constructor, you should just default initialize the variables. Then in your loop call a "set" member function to set the new values.

    Jim

  3. #3
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Quote Originally Posted by jimblumberg View Post
    remember when you defined the array the default constructor is called

    Jim
    Indeed, I didn't. Many thanks, works perfectly now. For those interested, here's the code as per Jim's suggestion:

    Code:
    #include<iostream>
    using namespace std;
    
    
    const int MEMB = 3; 
    
    
    class Dog{
    	private:
    		string m_name;
    		int m_age;
    	public:
    		Dog():m_name("doggy"), m_age(0){} 
    		void setDog(); 
    		void showDog(); 
    };
    
    
    int main(){
    	
    	Dog list[MEMB];
    	for (int i = 0; i < MEMB; i++){
    		list[i].setDog();
    		cout<<endl; 
    	}
    	cout<<endl;
    	for (int i = 0; i < MEMB; i++){
    		list[i].showDog();
    		cout<<endl; 
    	}
    }
    
    
    void Dog::setDog(){
    	cout<<"Enter dog's name: ";
    			string name;
    			cin>>name;
    			m_name = name;
    	cout<<"Enter dog's age: ";
    			int age;
    			cin>>age;
    			m_age = age; 
    }
    void Dog::showDog(){
    	cout<<"The dog's name is: "<<m_name<<endl;
    	cout<<"The dog's age is: "<<m_age<<" years"<<endl; 
    }

  4. #4
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    I know this is nitpicky but I also recommend getting array from C-style arrays and use std::array when you can as it gives you a lot more flexibility and power.

  5. #5
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    I'd also recommend getting away from raw arrays, but I would recommend you switch to std::vector instead of std::array.

    Jim

  6. #6
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Thanks all, I've taken on board your kind suggestions and re-worked the above using vector and iterator, this was useful learning.

    For those still interested, code below:

    Code:
    #include<iostream>
    #include<vector>
    using namespace std;
    
    
    const int MEMB = 3; 
    
    
    class Dog{
    	private:
    		string m_name;
    		int m_age;
    	public:
    		Dog():m_name("doggy"), m_age(0){} 
    		void setDog(); 
    		void showDog(); 
    };
    
    
    int main(){
    	
    	vector<Dog> list (MEMB); 
    	for (vector<Dog> ::iterator it = list.begin(); it != list.end(); ++it){
    		(*it).setDog();
    		cout<<endl; 
    	}
    	cout<<endl;
    	for (vector<Dog>::iterator it = list.begin(); it != list.end(); ++it){
    		(*it).showDog();
    		cout<<endl; 
    	}
    }
    
    
    void Dog::setDog(){
    	cout<<"Enter dog's name: ";
    			string name;
    			cin>>name;
    			m_name = name;
    	cout<<"Enter dog's age: ";
    			int age;
    			cin>>age;
    			m_age = age; 
    }
    void Dog::showDog(){
    	cout<<"The dog's name is: "<<m_name<<endl;
    	cout<<"The dog's age is: "<<m_age<<" years"<<endl; 
    }

  7. #7
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Okay, but you may want to start using range based loops as well.

    Code:
        vector<Dog> list (MEMB); // Also note that the size of your vector doesn't need to be a constant. 
        // Instead of:
        for (vector<Dog> ::iterator it = list.begin(); it != list.end(); ++it){
            (*it).setDog();
            cout<<endl; 
        }
    
        for(auto& itr : list)
            itr.setDog();
    And perhaps overload the extraction operator to print the data as well.


    Jim

  8. #8
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Actually, range-based for-loops were the entire reason I suggested std::array XD

    But as for array vs vector...

    (This is more for the OP than anyone else)

    Only use array if and only if the size is known at compile-time and you know for a fact that the array won't overflow your stack size and you don't mind each element being default constructed.

  9. #9
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Actually, range-based for-loops were the entire reason I suggested std::array XD
    You can use range-based for() loops with std::vector, std::array, or even raw arrays that have been statically allocated in the same scope as the loop.

    The reason I suggest std::vector over std::array is because std::vectors don't need a compile time constant for their sizes and std::vectors are easier to use when writing reusable code.

    Jim

  10. #10
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    But if you do have a compile time constant size, then an array is more efficient. It often eliminates a level of indirection, and some additional calculation. Arrays are a simpler abstraction.

    However, vector is a good default container. If you don't want to think about it, just always use a vector.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  11. #11
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Quote Originally Posted by jimblumberg View Post
    You can use range-based for() loops with std::vector, std::array, or even raw arrays that have been statically allocated in the same scope as the loop.

    The reason I suggest std::vector over std::array is because std::vectors don't need a compile time constant for their sizes and std::vectors are easier to use when writing reusable code.

    Jim
    Can you really use range-based syntax with a raw array? I thought it was just syntactic sugar for the `begin` and `end` methods.

    I don't also see why a vector would be better when writing reusable code. If you can write your algorithms in terms of a vector, you know that you can implement all your algorithms in terms of _at most_ RandomAccessIterator which is pretty flexible. Because of the iterator-based approach, actual choice of container is relatively irrelevant.

    Plus, std::array is on the stack and that is an advantage.

  12. #12
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Hello again, if the size of the vector was not known at compile time, rather if I was told to keep entering Dog objects into the vector until a blank string is entered for a Dog's name, how should I code it?

    My pseudo-code / thinking is as follows:

    1. declare (a) vector of Dog objects, dog_list, (b) generic dog object, d, (c) iterator it;
    2. push_back the vector with the generic dog object d;
    3. use the iterator and the "set" member function to set the latest entered generic dog's data members as desired;
    4. check if the name of the latest dog is blank; if not, continue, if blank, break;

    Based on this, I have the following code that compiles but aborts after the first dog's details have been entered. Any suggestions would be very welcome. Thanks

    Code:
    #include<iostream>
    #include<vector>
    using namespace std;
    
    
    class Dog{
    	private:
    		string m_name;
    		int m_age;
    	public:
    		Dog():m_name("doggy"), m_age(0){} 
    		void setDog(); 
    		void showDog(); 
    		string nameDog() { return m_name;}
    };
    
    
    int main(){
    	
    	Dog d; 
    	vector<Dog> dog_list; 
    	vector<Dog> ::iterator it;
    	
    	do{
    		dog_list.push_back(d);
    			for (it = dog_list.begin();  ; ++it){
    				(*it).setDog();
    			}
    			cout<<endl;
    	}
    	while ((*it).nameDog() != " ");
    	 
    	cout<<endl;
    	
    	for (vector<Dog>::iterator it = dog_list.begin(); it != dog_list.end(); ++it){
    		(*it).showDog();
    		cout<<endl; 
    	}
    }
    
    
    void Dog::setDog(){
    	cout<<"Enter dog's name: ";
    			string name;
    			cin>>name;
    			m_name = name;
    	cout<<"Enter dog's age: ";
    			int age;
    			cin>>age;
    			m_age = age; 
    }
    void Dog::showDog(){
    	cout<<"The dog's name is: "<<m_name<<endl;
    	cout<<"The dog's age is: "<<m_age<<" years"<<endl; 
    }

  13. #13
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Hello again, if the size of the vector was not known at compile time, rather if I was told to keep entering Dog objects into the vector until a blank string is entered for a Dog's name, how should I code it?
    I'd actually recommend using `std::list` or just using `std::vector` with the `push_back` method. `push_back` will automatically reallocate your vector. This is typically pretty slow though so I think a list might actually be a more suitable container.

  14. #14
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    It aborts because of this code here:
    Code:
     for (it = dog_list.begin();  ; ++it)
    This is an infinite loop, that starts at the first element of the dog_list and iterates beyond the end, causing writes to memory you do now own.

    It sounds like you don't need a for loop here at all, since you only want to set let last element. Also you should add the dog to the dog list only after you determine that it's not blank.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  15. #15
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by MutantJohn View Post
    I'd actually recommend using `std::list` or just using `std::vector` with the `push_back` method. `push_back` will automatically reallocate your vector. This is typically pretty slow though so I think a list might actually be a more suitable container.
    Disagree completely. std::list is slow. Even a poor implementation of std::vector will outperform std::list in most cases, because std::list will scatter your elements in memory, which cause lots of cache misses to access subsequent elements. That's much worse than the reallocation penalty as the container grows. It's worse even than the cost of inserting an element in the middle of a vector. Your cpu is much better at reading sequentially ordered memory like a vector than the node structure of a container such as std::list.

    Furthermore, problems with reallocation caused by increasing vector size can more easily be eliminated by simply reserving the capacity of the vector in advance.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Initializing an array within a class
    By Sijaan Hallak in forum C++ Programming
    Replies: 1
    Last Post: 12-28-2015, 02:53 PM
  2. Replies: 5
    Last Post: 01-06-2015, 09:04 AM
  3. Initializing a 3D array w/o a loop.
    By funkydude9 in forum C++ Programming
    Replies: 5
    Last Post: 03-04-2004, 11:20 PM
  4. Replies: 4
    Last Post: 10-16-2003, 11:26 AM
  5. syntax for initializing an array in a class
    By timberwolf5480 in forum C++ Programming
    Replies: 4
    Last Post: 10-14-2003, 08:03 AM

Tags for this Thread