Thread: Initializing member arrays in constructors?

  1. #1
    Registered User
    Join Date
    Apr 2004
    Posts
    72

    Question Initializing member arrays in constructors?

    I am curious how to go about intializing a member variable if it is an array, and the size isn't given until it compilation. Doing so would better encapsulate the code which is the point of thise whole practice.

    Here's a simple code example...

    Code:
    #ifndef STACK_H
    #define STACK_H
    
    #include <iostream>
    
    #define SIZE		100
    
    class Stack
    {
    private:
    	int m_stck[SIZE];
    	int m_tos;
    public:
    	Stack();
    	void Push(int i);
    	int Pop();
    };
    
    #endif
    What I am interested in is having Stack's constructor take in a "size" arguement and pass it into m_stck. This way, there's one less define constant in the program. And of course, I can set each Stack object to different max sizes which would be a nice benefit. Some may just need 20, some may need 100. But just figuring out how to do this in general would be great to know down the road. Something like this is what I'm looking for...

    Code:
    Stack::Stack(int size) {
       // pass size into m_size[]'s brackets. 
    }
    Please note I'm using Stack as an example. I can see many classes that might require a similar task. Mainly what I'm trying to understand is how to pass a variable into a member array within an object. If I didn't, SIZE would always be the same value!
    Last edited by philvaira; 07-25-2007 at 02:17 AM.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    the size isn't given until it compilation
    You mean, SIZE would be a compile-time constant? Another option might be a static const in the class. (Just having a const member is not enough, because these can be initialized at run-time with a variable, but static array sizes need to be known at compile-time.)

    It should also be possible to use a template parameter for size - like std::bitset - to generate code for Stack objects of varying size, but all the different sizes need to be known while compiling.

    If you mean you want SIZE to be determined at run-time, you'll need to go with dynamic allocation (or may-be use std::vector as the underlying container for the stack as it manages the memory for you).

    Note that some compilers may allow declarations of static arrays with variable run-time size (because C allows it) but that is not standard and portable.
    Last edited by anon; 07-25-2007 at 02:37 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    Registered User
    Join Date
    Apr 2004
    Posts
    72
    I'm not 100% sure if I understand. I'm leaving out templates for now just for clearity. I'll try to explain what I'm trying to do better in a higher level language.

    In C#, it was like this...
    Code:
    int[] m_var = null;
    public MyConstructor(int myRequestedSize) { 
       m_var = new int[myRequestedSize];
    }
    Hmm, in C++ wouldn't it be this?
    int *var;
    var = new int[myRequestedSize];

  4. #4
    Registered User
    Join Date
    Apr 2004
    Posts
    72
    I'm the smartest man alive

    In main(), I did this...
    Code:
    	Stack s(10);  // give it a size of 10
    
    	s.Push(1);
    	s.Push(2);
    	s.Push(3);
    
    	std::cout << s.Pop() << std::endl;
    	std::cout << s.Pop() << std::endl;
    	std::cout << s.Pop() << std::endl;
    Stack's header:
    Code:
    #ifndef STACK_H
    #define STACK_H
    
    #include <iostream> 
    
    class Stack
    {
    private:
    	int *m_stck;
    	int m_tos;
    	int m_size;
    public:
    	Stack(int size);
    	~Stack();
    	void Push(int i);
    	int Pop();
    };
    
    #endif
    And Stack.cpp:
    Code:
    #include "stack.h"
    
    // Stack constructor
    Stack::Stack(int size)
    {
    	m_tos = 0; 
    	m_stck = new int[size];
    	m_size = size;
    }
    
    Stack::~Stack() 
    {
    	if (m_stck) {
    		delete m_stck; 
    		m_stck = NULL;
    	}
    }
    
    // Pushes a value onto the stack.
    void Stack::Push(const int i) 
    {
    	if (m_tos == m_size) {
    		std::cout << "Stack is full." << std::endl;
    		return;
    	}
    
    	m_stck[m_tos] = i;
    	m_tos++;
    }
    
    // Pops a value from the stack.
    int Stack::Pop()
    {
    	if (m_tos == 0) {
    		std::cout << "Stack underflow." << std::endl;
    		return -1;
    	}
    
    	m_tos--;
    	return m_stck[m_tos];
    }
    Last edited by philvaira; 07-25-2007 at 02:57 AM.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I noticed:
    Code:
    Stack::~Stack() 
    {
    	if (m_stck) {
    		delete m_stck; 
    		m_stck = NULL;
    	}
    }
    Deleting a null pointer is safe, so you can delete m_stck even if it is null. However, m_stck was allocated with new[], so you must use delete[] on it, not delete.
    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
    Apr 2004
    Posts
    72
    Okay, thanks. I'm a bit confused confused with when to use delete [] and just delete. Got any examples on when to choose? Is [] just for arrays? Thanks a ton.

    Oh, I think I know...
    int *p = new int; // call delete p afterwards.
    int *p = new int[size]; // call delete [] p
    Last edited by philvaira; 07-25-2007 at 03:12 AM.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Pair new with delete and pair new[] with delete[]. new[] would be used for dynamic arrays.

    Just a note: you are actually assigning values in your constructor, not initialising the member variables. You should use an initialisation list instead.
    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
    Apr 2004
    Posts
    72
    I'm not sure if I know what you mean.

    m_stck = new int[size]; // this gives it the max size to store
    m_stck[1]; // this hasn't been given a value yet since m_tos is at 0 initialially. Push(int i) would do the assigning.

    Are you suggesting to clear the dynamic array out with a value (like -1)?

    I think something like this second constructor might be good though...
    Stack(int size, int initValue);

    initValue would do something like this...
    Code:
    for (int i = 0; i < m_size; i++) { 
      m_stck[i] = initValue; 
    }
    Push() should receive an unsigned value too. Ah well, it's all extra candy now that I could add. I'm just glad to know I got the dynamic array within a class down.
    Last edited by philvaira; 07-25-2007 at 03:32 AM.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I had in mind:
    Code:
    // Stack constructor
    Stack::Stack(int size) : m_tos(0), m_stck(new int[size]), m_size(size)
    {
    }
    Especially since size is an int instead of an unsigned int, you might want to delay the new[] until the constructor body or you can validate m_size (and provide a default size instead, if it is non-positive).
    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
    Apr 2004
    Posts
    72
    That's a good idea. I'm wondering if the other way is more readable though. But for a few variables being assigned that way isn't too big of a deal to add.

    I'm curious when to use that option though. Is it just when there's a few variables to store? Would you use the block if you need to check for validity? For example, it might be a good idea to throw an exception if there's not enough memory for the array allocation.
    Last edited by philvaira; 07-25-2007 at 03:43 AM.

  11. #11
    Registered User
    Join Date
    Apr 2004
    Posts
    72
    There's another thing that I don't get. The Stack header doesn't have the inline keywords by Push and Pop, but when I include them in the .cpp file, I'm getting these errors:

    main.obj : error LNK2019: unresolved external symbol "public: int __thiscall Stack::Pop(void)" (?Pop@Stack@@QAEHXZ) referenced in function _main
    main.obj : error LNK2019: unresolved external symbol "public: void __thiscall Stack::Push(unsigned int)" (?Push@Stack@@QAEXI@Z) referenced in function _main

    Code:
    // Pushes a value onto the stack.
    inline void Stack::Push(const unsigned int i) 
    {
    	if (m_tos == m_size) {
    		std::cout << "Stack is full." << std::endl;
    		return;
    	}
    
    	m_stck[m_tos] = i;
    	m_tos++;
    }
    
    // Pops a value from the stack.
    inline int Stack::Pop()
    {
    	if (m_tos == 0) {
    		std::cout << "Stack underflow." << std::endl;
    		return -1;
    	}
    
    	m_tos--;
    	return m_stck[m_tos];
    }
    It's odd because I am looking a code example right infront of me. It works in a single file but having two like this seems to be the problem for some reason. Any idea why that would come up?

  12. #12
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I'm afraid that if you want to inline these functions you'll need to provide the implementation in the header file (in the class declaration, where they would be implicitly inlined). The inlined function implementation needs to be in the same compilation unit where it is used (compilation unit = e.g main.cpp + headers that main.cpp includes + headers these headers might include).
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  13. #13
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    There's no reason I can think of to use dynamic arrays (i.e. new[] and delete[]) here instead of a vector. Even as a learning exercise using a vector would be more productive.

    If for whatever reason you decide to continue using dynamic arrays, lookup the rule of three. What it basically says is that you need to write a copy constructor and copy assignment operator (or you need to make them disabled). If you use a vector you wouldn't need to write either, and you also wouldn't need to write the destructor. Everything would work automatically.

    --------------------------------------------

    >> I'm curious when to use that option though.
    You should always use the initializer list when you can (which is almost always). It is the same or more efficient. It may seem less clear to you, but it has the extra bonus of making it obvious that you are initializing those variables. Code inside the block might be initializing or doing other processing. In that sense many people would consider the intializer list to be clearer.

    >> Would you use the block if you need to check for validity?
    Perhaps. Many class constructors perform other work inside the block. Just the initialization of the member variables is what should be done in the initializer list.

    >> For example, it might be a good idea to throw an exception if there's not enough memory for the array allocation.
    Unless you have an old compiler or set an option to do otherwise, an exception is already automatically thrown if not enough memory is available.

    >> I'm wondering if the other way is more readable though.
    If you have lots of member variables, a common way to organize them is like this. Note that you should keep them in the same order as they are declared in the class, since the order in the class is the order they are initialized, not the order in the list:
    Code:
    // Stack constructor
    Stack::Stack(int size) :
        m_tos(0),
        m_stck(new int[size]),
        m_size(size),
        m_another_variable1(),
        m_another_variable2(0),
        m_another_variable3(0.0),
        m_another_variable4("[none]"),
        m_another_variable5(size)
    {
    }

  14. #14
    Registered User
    Join Date
    Apr 2004
    Posts
    72
    Vectors are good if you don't know how much might be in the stack. If you know it's going to be around 20 items, chances are performance is slightly better. If you don't know how much might be in it, vectors are great for this and I would choose it anytime in that case. Let the data structure fit the problem.

    Thanks for explaining that. It does still seem odd to me. Say you pass in a integer and use the initialization list. But later you decide to do some checking on it... say it only accepts 0 to 5. If you pass in 6, there's some conditioning to check. So, if you knew you might have to change the logic later (which I would think is pretty common in checking arguements), isn't it better to just store it within the block like this? It just seems like half of the arguements would be in the init list and the rest in the body... maybe harder to find later? It's just new to me.

    Code:
    class Customer { 
    public:
       Customer(unsigned int password) { 
             if ((password >= 0) && (password < 5000))
                   m_password = password;
              }
    }

  15. #15
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Vectors are good if you don't know how much might be in the stack. If you know it's going to be around 20 items, chances are performance is slightly better.
    Well, not necessarily. Vector could be beaten if it needs to reallocate, but you can avoid that by using reserve. And if your estimate is wrong (you're prepared for 24, but you actually need 25), you're in trouble, because it would be hard to write a reallocation mechanism as good as that of a vector (unless may-be you are writing it for a specific simple type and not a templated Stack).

    It just seems like half of the arguements would be in the init list and the rest in the body... maybe harder to find later? It's just new to me.
    I'm not sure what you mean here. Do you think it is confusing to have things initialized in two places? Now only half of them will be hard to find. But if they all were in the body, all would be harder to find.

    I guess a programmer must live with it, that in code it is hard to find things...

    Or do you mean that you're running a risk of not initializing m_password in some cases? But then it is your problem as a programmer to solve. Throw an exception if there's no sensible default value for m_password and you can't put the object in a good state. Or else, if a default value is meaningful, you might use it to initialize the member to it in the initialization list.

    Nitpick:
    Code:
       Customer(unsigned int password) { 
             if ((password >= 0) && (password < 5000))
                   m_password = password;
              }
    Last edited by anon; 07-25-2007 at 04:41 PM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Initializing a private array member
    By csonx_p in forum C++ Programming
    Replies: 13
    Last Post: 09-18-2008, 10:44 AM
  2. Can you check what is wrong with this code
    By Ron in forum C++ Programming
    Replies: 4
    Last Post: 08-01-2008, 10:59 PM
  3. Initializing Objects with constructors
    By freddyvorhees in forum C++ Programming
    Replies: 1
    Last Post: 07-24-2008, 07:11 AM
  4. Constructors for creating 2d arrays
    By Welshy in forum C++ Programming
    Replies: 5
    Last Post: 06-29-2005, 03:50 PM
  5. Replies: 14
    Last Post: 03-17-2003, 10:07 AM