Thread: Initializing array of class objects via loop

  1. #16
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    But if you do have a compile time constant size, then an array is more efficient.
    Yes a raw array can be more efficient, however IMO the fact that you must always pass the size into a function along with the array erases most if not all of those efficiencies. When dealing with a std::array most of the effiencies are also lost if you try to write reusable code, code that can be used with any array size.

    However, vector is a good default container.
    IMO std::vector should be the default vector, only use something else if there is a good reason. For example a list may be a better choice if you don't need random access and you will be inserting and removing elements to to other than the back of the container.

    Can you really use range-based syntax with a raw array? I thought it was just syntactic sugar for the `begin` and `end` methods.
    Yes, you can use range based loops with raw arrays, as long as the array definition is in the same scope (it hasn't decayed to a pointer). You can't use ranged based loops with a raw array that is passed into a function for example.

    I don't also see why a vector would be better when writing reusable code.
    Because you can pass a std::vector to a function no matter the size of the vector. With a std::array you need some "magic" to be able to write a function that can take a std::array of any size.

    Plus, std::array is on the stack and that is an advantage.
    It is only really an advantage when the array is small enough for the stack. I feel that because a std::vector places the elements on the heap is an advantage, not a disadvantage.

    Code:
     
        do{
            dog_list.push_back(d);
                for (it = dog_list.begin();  ; ++it){
                    (*it).setDog();
                }
                cout<<endl;
        }while ((*it).nameDog() != " ");
    Why not something more like:
    Code:
    ...
         while (d.setDog())
        {
             dog_list.push_back(d);
        }
          
        cout<<endl;
         
        for (auto& it : dog_list){
            it.showDog();
        cout<<endl; 
    ...
    
    bool Dog::setDog(){
        cout << "Enter dog's name: ";
        cin >> m_name;
    
        cout <<"Enter dog's age: ";
        cin >> m_age;
        return m_name.empty(); 
    }

    By the way Dog::showDog() should also be const qualified.


    Jim

  2. #17
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by jimblumberg View Post
    Yes a raw array can be more efficient, however IMO the fact that you must always pass the size into a function along with the array erases most if not all of those efficiencies. When dealing with a std::array most of the effiencies are also lost if you try to write reusable code, code that can be used with any array size.
    You don't need to pass the size of an std::array to a function; std::array's size is part of its type. To use std::array with code that allows any size array, you need a template function. Doing so does not lose you any runtime performance. It is part of the purpose of arrays that their size is know at compile time, if that's not what you want, obviously don't use it.

    Yes, passing old style bracketed arrays has the disadvantages you describe unless you work around them, but that is precisely was std::array was created.

    IMO std::vector should be the default vector, only use something else if there is a good reason.
    Seconded.

    For example a list may be a better choice if you don't need random access and you will be inserting and removing elements to to other than the back of the container.
    Disagree. I would still use std::vector. Locality of reference trumps the costs of copying elements.
    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.

  3. #18
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    You don't need to pass the size of an std::array to a function; std::array's size is part of its type. To use std::array with code that allows any size array, you need a template function.
    True but that "template function" is the magic I alluded to in my earlier post. Also because of the limited size and the requirement of using a compile time constant when defining the array, I recommend that you first consider using a std::vector. If used properly a std::vector has, IMO, adequate speed for all but the most demanding of applications.

    I would still use std::vector. Locality of reference trumps the costs of copying elements.
    Overall I would agree, if the vector/list is very large, and if copying elements is expensive a list may be a better choice when inserting and deleting many elements. However you should properly profile both containers before making the final decision if speed seems to be a problem.

    Jim

  4. #19
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Oh God, a container war.

    You guys, iterators were supposed to prevent this...

  5. #20
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    iterators were supposed to prevent this
    How?

    Just using iterators won't help you select the proper container for the job.


    Jim

  6. #21
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Actually, I think you can std::back_inserter to create a std::back_insert_iterator which is of type OutputIterator so you can use it with algoriths like std::fill_n or std::generate_n. I think you can also just assign to it directly.

    Edit:

    This is a good thing though. It'll fully allow the OP to decouple storage methods from algorithms while still being able to be performant.

    std::back_insert_iterator - cppreference.com
    std::back_insert_iterator:perator= - cppreference.com

    If you look at the overload for operator=, it works really well! This means the algorithm can be re-written in terms of vector or list. Unfortunately, std::array seems to only support in-place mutation of its elements. There needs to be a std::stack_vector.
    Last edited by MutantJohn; 09-14-2016 at 09:46 AM.

  7. #22
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    And how will all that "magic" help you select the proper base container? Even writing your own iterators will not overcome all of the differences between the base containers, nor will iterators solve some of the inherent issues with the different containers. The reason there are several containers is because each container has it's own set of pluses and minuses that need to weighed to determine the proper container. Unless you change the behavior or the base container you are still stuck with any limitations inherent to the container chosen. For example a std::list can place the "new" elements anywhere in memory where it has room, which as already pointed out can cause performance problems caused by cache misses.


    Jim

  8. #23
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    It's silly to worry about your cache lines for the context of this program. For one, I doubt the input will ever be large enough to have a significant impact. Second, it's I/O-based now. The instant you start doing anything I/O-based, your CPU is never really the problem. For one, you're throttled by the user's ability to input data.

    If this was some intense computational code on a large dataset then yes, it's fine to introduce cache-friendly optimizations. But in this case, it's entirely overkill. Does the OP even know what a cache line is?

    I suggested a list because it's more semantic. Lists were sort of built for random, continuous additions to them without having to worry about anything like a relocation scheme. Yes, vectors can reallocate. That's fine. Just make sure you that your class's move constructor and move assignment operator are noexcept specified so that way the reallocation can at least move the elements instead of copying.

    My real point is becoming that C++ was designed with the idea that you decouple storage from algorithms which is why C++ is so iterator-heavy. With the back_insert_iterator, it's now trivial to dynamically switch containers so it's almost a moot point.

  9. #24
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Unless you have to worry about optimization, I would argue that you should just use std::vector. It's not just about std::vector performing better. It has constant access to everywhere, and therefore usually don't cause any surprises anywhere. std::list is better... in theory. But cache misses cost a lot more than some sequential copying.

    Bjarne pointed out in a keynote for Cppcon (I believe?) that std::vector is just faster in most cases. In fact, it's surprising how much it actually beats std::list most of the time. So. Just use std::vector. Dot. If your code is slow, and you can determine it's due to your vector usage, then try substituting in a list. Time it, see what happens. It may be faster. It might not. But unless it's a performance problem, just don't bother. std::vector is good enough, most of the time, so you don't have to both picking out a specific container.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #25
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    @jimblumberg, many thanks for your suggestion (#16) and the earlier re range loops. bool is indeed the way to go in this case, but note that m_name.empty() returns false if a dog WITH name is entered and hence terminates the loop in this case. So I've tweaked your code slightly which works fine with one caveat though, checking for an empty name string (either m_name = "" or m_name = " ") doesn't seem to work and so I'd to replace the string to quit as "Exit" in this case (hoping that there isn't a dog called Exit out there though this does leave a chink in the code). Thanks once again.
    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){}
            Dog(const Dog& rhs);
            bool setDog();
            void showDog() const;
            string nameDog() { return m_name;}
    };
    
    int main(){
    
        Dog d;
        vector<Dog> dog_list;
     
      while (d.setDog())
        {
             dog_list.push_back(d);
        }
        cout<<endl;
    
       for (auto& it : dog_list){
            it.showDog();
        cout<<endl;
        }
    }
    Dog::Dog(const Dog& rhs){
        m_name = rhs.m_name;
        m_age = rhs.m_age;
    }
    bool Dog::setDog(){
        cout << "Enter dog's name (Exit to quit): ";
           cin >> m_name;
        if(m_name == "Exit"){
           return false;
       }
        cout <<"Enter dog's age: ";
        cin >> m_age;
    
           return true;
    }
    
    void Dog::showDog() const{
        cout<<"The dog's name is: "<<m_name<<endl;
        cout<<"The dog's age is: "<<m_age<<" years"<<endl;
    }
    Last edited by sean_cantab; 09-14-2016 at 09:11 PM.

  11. #26
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    You may want to try something like the following for setDog().

    Code:
    bool Dog::setDog(){
        cout << "Enter dog's name (Enter to quit): ";
        getline(cin, m_name);
    
        if(m_name.empty()){
           return false;
       }
        cout <<"Enter dog's age: ";
        cin >> m_age;
        cin.ignore(1000, '\n');
    
        return true;
    }
    Jim

  12. #27
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Out of curiosity though, but must setDog really have no parameters? I would suggest that setDog have parameters for name and age, possibly still with a bool return value to indicate invalid arguments (or you could simply throw an invalid argument exception, if you have learnt about exceptions). Then, you write a non-member non-friend function for obtaining the name and age of the dog from input, hence decoupling the input method and format from the modeling of a dog.
    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

  13. #28
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    decoupling the input method and format from the modeling of a dog
    I agree, separating the UI from the class implementation would be a much better design.

    Jim

  14. #29
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Thanks, I'd also read an example where a setter function was non-member (that was for a struct though but the idea was similar) and the code below is on those lines. However I also recall reading, most likely Scott Meyers, that all functions should be member functions as far as possible apart from the overloaded >>, << etc but you'd recommend separating the UI in this case because of the way in which data is input into the modelling of a Dog object?

    Code:
    bool setDog(Dog& d){
    	cout<<"Enter dog name (Enter to quit)"<<endl;
    	string temp_string;
    	getline(cin, temp_string);
    	if(temp_string.empty()){
    		return false;
    	}
    	d.setName(temp_string);
    	cout<<"Enter dog's age: "<<endl;
    	int temp_age;
    	cin>>temp_age;
    	d.setAge(temp_age);
    	cin.ignore(1000,'\n');
    	return true; 	
    }

  15. #30
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by sean_cantab
    I'd also read an example where a setter function was non-member (that was for a struct though but the idea was similar) and the code below is on those lines.
    Assuming default member access, given that it was for a struct, the setter function probably was a non-member because it didn't need to be a member, i.e., the non-member was enough to access the member variable(s). For your typical class, the member variable would be private, so the setter would need to be a member (or a friend function, but that would be unusual).

    Quote Originally Posted by sean_cantab
    However I also recall reading, most likely Scott Meyers, that all functions should be member functions as far as possible apart from the overloaded >>, << etc but you'd recommend separating the UI in this case because of the way in which data is input into the modelling of a Dog object?
    How Non-Member Functions Improve Encapsulation by Scott Meyers
    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. 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