Allocating Memory from a Constructor

Started by
8 comments, last by snk_kid 19 years, 7 months ago
Well, I've done about a dozen searches (both Google and Gamedev), and still haven't found a satisfying answer. I am designing a CMatrix class (implementing numerous mathematical matrix operations, etc). My base constructor, CMatrix(), very much stripped down... is as follows:

CMatrix::CMatrix() {
// 1x1 matrix
m_NumRows = 1;
m_NumCols = 1;

m_pData = NULL;
m_pData = AllocateMemory( m_NumRows, m_NumCols );
}

AllocateMemory() obviously allocates enough space for Rows*Columns, and sets all the entries to 0. This can fail. The options I've come up with from searches are: 1). Don't do something that can fail in the constructor. This would mean, that I would have to call some Init() function for each matrix I create. I do NOT want to do this. 2). Use ASSERT()... but this only works in debug mode, so this isn't really an option. 3). Try/Catch blocks... But then this goes against the fundamental principle that many people preach, i.e. not allowing something that can fail to exist in a constructor. If I were to go this route, how exactly would I implement it in my example? Thanks! :)
Advertisement
#include <exception>CMatrix::CMatrix() {// 1x1 matrixm_NumRows = 1;m_NumCols = 1;m_pData = NULL;m_pData = AllocateMemory( m_NumRows, m_NumCols );if(m_pData == 0)    throw std::bad_alloc()}


try{   CMatrix matrix = new CMatrix();}catch(std::exception &e){   std::cout << "Problem creating Matrix" << e.what() << std::endl;}


std::bad_alloc inherits std::exception, which is why you can catch that. Alternatively you can catch a std::bad_alloc if that is all you want to catch, or even better you can make your own exception class and throw & catch that, then you wont have to worry about whether the `new CMatrix' failed.

Also, I'm not sure whether you need to delete the object yourself if it threw from the constructor.
Quote:Original post by Doggan
3). Try/Catch blocks... But then this goes against the fundamental principle that many people preach, i.e. not allowing something that can fail to exist in a constructor.


I disagree with this preaching (and as a matter of fact I've never heard it before in my life). It should work fine if people use RAII correctly:

What if you're making a vector class that needs to allocate memory, but it runs out? Throw an exception!!!

What if you've created a bounded integer for debugging purpouses, and the user tries to initialize that integer with an out of bounds integer? throw an exception!!!

What if you've created a singlteon that depends on another, but it hasn't been initialized yet (when it should have been explicitly)? throw an exception!!!

</exception_propaganda>
If you're worried about memory allocation failing, I believe new will throw an exception anyway (I think this is default as there is an option for it not to throw any exceptions.)

In regards to constructors shouldn't throw exceptions, I haven't heard of this one. However you're destructor should NEVER throw an exception, perhaps this is what you heard? There was a site that listed why the destructor shouldn't throw an exception, it was something to do with the fact that once an exception is thrown, the stack will unwind and calling the destructors of any objects that automaticcally go out of scope. If one of these destructors throws an exception, this process will get basically screwed!
Marshall Cline says, go ahead and throw an exception from your constructor.

(Although if the only thing that might fail is a memory allocation, which will fail by throwing std::bad_alloc anyway, then just let that exception fall through. In the simple case, at least. You might need to catch, do stuff and re-throw; keep reading...)

As mentioned, it's *destructors* which should never ever throw an exception.

(One of the most retarded things I've found in the Java library is that its stream-type objects have a habit of declaring that they can throw IOException from the .close() method. It shouldn't actually be possible, and there's no sane way to handle the situation if it did somehow happen, and it's a checked exception so you *have* to do *something*, or else propagate the throws clause all the way through.)

However, you do need to clean up after yourself if an exception happens.

That means that if you do several allocations, and allocation 0..x-1 succeed but allocation x fails, you have to deallocate 0..x-1 - which involves determining x, and making sure that you don't screw anything else up (did you already allow these allocations to interact with the outside world?).
if your matrix type does not grow then you don't need to dynamically allocate the memory for example an m-by-n matrix type:

#include <cstddef>template < typename T, size_t M, size_t N = M >class matrix {   enum { cols = M, rows = N, _size = M * N };   T _n[_size];public:    /* ...omitting code .. */};int main() {   //create 10-by-10 matrix   matrix< float, 10 > mat;   //create 5-by-6 matrix   matrix< float, 5, 6 > mat2;   /* ... omitting code ... */    return 0;}


if your going to be using square matrices that are less than a 5-by-5 only then you can build your matrices in terms of vectors with both subcript and named variable notations at no cost have alook here.
Quote:Original post by snk_kid
if your matrix type does not grow then you don't need to dynamically allocate the memory for example an m-by-n matrix type:

But your example uses templates, which means you can not multiply matrices of different dimensions, (though I don't suppose one really would want to, generic matrix multiplication is not very fast.)

Quote:Original post by Doggan
AllocateMemory() obviously allocates enough space for Rows*Columns, and sets all the entries to 0. This can fail.

How likely is it that memory allocation will fail? While you need to handle the failure for robustness, I don't think that on a modern machine with oodles of ram it is a realistic worry, and if there is not enough free memory on your system to allocate a five-by-five matrix, perhaps
exit(127);
would be appropriate ;)

SwiftCoder

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by swiftcoder
Quote:Original post by snk_kid
if your matrix type does not grow then you don't need to dynamically allocate the memory for example an m-by-n matrix type:

But your example uses templates, which means you can not multiply matrices of different dimensions, (though I don't suppose one really would want to, generic matrix multiplication is not very fast.)


Yes you can by using a template member function or free function.
Thanks for the replies.

I've implemented a simple try/catch block similar to the one suggested by Xetrov and it is working well.

My original comment:
Quote:3). Try/Catch blocks... But then this goes against the fundamental principle that many people preach, i.e. not allowing something that can fail to exist in a constructor.


was replied to by...
Quote:MaulingMonkey - I disagree with this preaching (and as a matter of fact I've never heard it before in my life).

desertcube - In regards to constructors shouldn't throw exceptions, I haven't heard of this one.


I got it from these very forums! :)
Quote:Oluseyi - Constructors never fail. Never.


Clicky, first reply, by none other than a mod! Heh. There was another thread that stated this as well, but I can't dig that one up at them moment.

Next... snk_kid, I allow the user the ability to add X amount of rows or columns to an already existing matrix. Dynamic memory was the easiest route to go for this one, as I saw it.

Quote:snk_kid - if your going to be using square matrices that are less than a 5-by-5 only then you can build your matrices in terms of vectors with both subcript and named variable notations at no cost have alook here.

swiftcoder - How likely is it that memory allocation will fail?


This class is not being designed as a game-oriented thing, really. Although I will probably incorporate it into my gaming projects and replace the DirectX matrix routines, it's designed to solve large mathematical problems dealing with linear equations. i.e. 20x20 matrices will not be uncommon. It's doubtful that the system will run out of memory, but still it is a possiblity.

Again, thanks for the replies. The try/catch is working, even if it goes against what Oluseyi and a couple others said. :)
Quote:Original post by Doggan
Next... snk_kid, I allow the user the ability to add X amount of rows or columns to an already existing matrix. Dynamic memory was the easiest route to go for this one, as I saw it.

This class is not being designed as a game-oriented thing, really. Although I will probably incorporate it into my gaming projects and replace the DirectX matrix routines, it's designed to solve large mathematical problems dealing with linear equations. i.e. 20x20 matrices will not be uncommon. It's doubtful that the system will run out of memory, but still it is a possiblity.


Thats fair enough, have you considered using a matrix library that uses expression templates? such as this one

Quote:Original post by swiftcoder
Quote:Original post by snk_kid
if your matrix type does not grow then you don't need to dynamically allocate the memory for example an m-by-n matrix type:

But your example uses templates, which means you can not multiply matrices of different dimensions, (though I don't suppose one really would want to, generic matrix multiplication is not very fast.)


I know i've already said you can but here is just one example code for doing it:

template < typename T, size_t P, size_t Q, size_t R >matrix< T, P, R > operator*(const matrix< T, P, Q >& m1, const matrix< T, Q, R >& m2) {   /* ... stuff here ... */}


so a p-by-q matrix * q-by-r matrix = p-by-r matrix.

for fast generic matrix multiplication, there is something called expression templates that use meta-template programming to evaluate stuff at compile time but it could take a long while to compile.

This topic is closed to new replies.

Advertisement