473,804 Members | 2,104 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

overloading the [] operator

Hello,

I've created a class to store 2 dimensional matrices, and I've been
trying to overload the [] operator, so access to the elements of the
matrix is easy. I have 3 private variables, _m, _row and _col which are
respectively the array to store the data in, the number of rows and the
number of columns. _m is of type T, and is size _row * _col * sizeof
(T).

I've managed to overload the subscript operator like this:
T & operator [] (int n)
{
return (_m[n * _col]);
}

but I don't understand why you can't do:
T * operator [] (int n)
{
return (&_m[n * _col]);
}

instead. The first one works fine, but with the second one when I try
something like m[0][0] = 100.0, m[0][0] comes up as being an invalid
lvalue.
Could someone help me out here?

Cheers,

- Joseph Paterson

Jun 3 '06
16 2514

Axter wrote:
Check out the following link for an example:
http://code.axter.com/dynamic_2d_array.h


STILL trying to pass that off as an example are you???

If you are going to show people how to implement something like that at
least put a little effort into it.

Jun 4 '06 #11

Kai-Uwe Bux wrote:
Axter wrote:
Kai-Uwe Bux wrote:
Noah Roberts wrote:

>
> Kai-Uwe Bux wrote:
>> Joseph Paterson wrote:
>>
>> > Hello,
>> >
>> > I've created a class to store 2 dimensional matrices, and I've been
>> > trying to overload the [] operator, so access to the elements of the
>> > matrix is easy. I have 3 private variables, _m, _row and _col which
>> > are respectively the array to store the data in, the number of rows
>> > and the number of columns. _m is of type T, and is size _row * _col
>> > * sizeof (T).
[snip]
>
> Also, I can't find it but it must be in the faq somewhere...all your
> variable names are broken. Don't use anything that starts with an
> underscore.
[reasonable recommendation snipped]

You cannot find it because it should not be there. The reserved
identifiers are [17.4.3.1.2]:

* any identifier that contains a *double* underscore "__".
* any identifier that begins with "_" followed by a *capital* letter.
* any identifier that begins with "_" is reserved in the *global
namaspace*.

So, for local variables and parameters, the names are fine.


You should never name a local variable with an underscore followed by
an upper case letter.

IAW C++ standards that you quoted, names that begin with an underscore
and are followed by an uppercase letter, can be used for anything by
the implementation.
That means the implementation can use macros for this.
Example:
#define _T(x) L##x

If you have a local variable with the name _T, it will fail to be
portable to an implementation that uses above macro.


Note: the names the OP used are not of this form! We are all in agreement
about names like "_T".

IAW C++ standard, a name that begins with an underscore and then
followed by a lower case letter are reserved for the implementation in
the global namespace.
So technically speaking, IAW C++ standard, you could name a local
variable that begins with an underscore and is followed by a lower case
letter. And your code should be portable.


That is why the OP's names are fine.
However, this is not true in practice, because some implementations
also use these types of names with macros, which invade all namespaces,
and not just the global namespace.


Those are broken implementations .
IAW with the C++ standard, the following code should be portable:
int foo(int _itot, int _ttoi)
{
return _itot + _ttoi;
}

But the above code is not portable to windows platform that reference
tchar.h, because this header has macros named _itot and _ttoi.


tchar.h is a not a standard header. If you use third-party libraries, all
sorts of new macros could be defined. No naming scheme will prevent clashes
with certainty.
So I recommend that leading undescore not be used at all, even in local
variables.


Although I would agree that the advice is sound and reasonable from a
practical point of view (and from an aesthetical angle, too: leading
underscores are plain ugly); I would also maintain that workarounds for
problems with tchar.h and other third-party headers are not exactly topical
in this group.


The standard referrs to what the implementation can do with leading
underscore names.
There's no way of giving an example of this, without actually referring
to a specific implementation.
This paticular windows header, is not an isolated example.
There are other implementations that incorrectly use leading underscore
names followed by a lowercase letter.
IMHO, this is within the scope and context of this group.

This is not a workaround for a paticular header. It's a workaround for
all implementations that incorrectly use leading underscores followed
by a lowercase letter.

Jun 4 '06 #12
Noah Roberts wrote:
Axter wrote:
Check out the following link for an example:
http://code.axter.com/dynamic_2d_array.h


STILL trying to pass that off as an example are you???

If you are going to show people how to implement something like that at
least put a little effort into it.


Still passing yourself as an expert in this topic.
If you think you can do better, then do-so, and post it as an example.

If you think you have a better link with the same interface, then post
that.

If you don't have any thing better, then I recommend you find something
better to do then wasting this group's time with your post.

Jun 4 '06 #13

Axter wrote:
Noah Roberts wrote:
Axter wrote:
Check out the following link for an example:
http://code.axter.com/dynamic_2d_array.h
STILL trying to pass that off as an example are you???

If you are going to show people how to implement something like that at
least put a little effort into it.


Still passing yourself as an expert in this topic.


Actually, no. You'll find you are the only one doing that here.
If you think you can do better, then do-so, and post it as an example.
template<typena me T>
struct dynamic_2d_arra y
{
typedef T** type;
};

Actually, I wouldn't even bother with that.

Tell me what your class offers over that.

If you think you have a better link with the same interface, then post
that.
Why would I? It already exists in the standard.

If you don't have any thing better, then I recommend you find something
better to do then wasting this group's time with your post.


You waste people's time every time you post that piece of crap and tell
newbies to do it that way. Like I said, if you are going to create an
example then at least spend a little time on it. If that code took
more that 5 seconds to write I would be surprised. I _know_ it hasn't
been tested.

Hint:

map<int, dynamic_2d_arra y<double> > mtx_map;

But you know what. I am going to take you up on this challenge. I'm
going to fix your code for you. That way when you want to tell people
to implement op[][] at least you will have a halfway decent
implementation that won't also display horrid coding practices and
numerous bugs.

Jun 4 '06 #14

Axter wrote:
Noah Roberts wrote:
Axter wrote:
Check out the following link for an example:
http://code.axter.com/dynamic_2d_array.h


STILL trying to pass that off as an example are you???

If you are going to show people how to implement something like that at
least put a little effort into it.


Still passing yourself as an expert in this topic.
If you think you can do better, then do-so, and post it as an example.


I am not posting this as an "example" as I don't agree with the idea.
I am also not posting it as an expert sample. I am posting this to
show some of the things a programmer should do differently than you did
if they insist on going that way and why your code "example" should not
be considered such.

If anyone believes the following code exhibits bad practice in any way
please speak up. I won't promise to agree but do listen and in cases
such as this I believe correctness is more important than my ego.

Note the use of whitespace!!

I did keep my lines to 80 chars but this "newsreader " may still break
the formatting. Does anyone have a suggestion of some place to put
this for better readability?

I don't normally comment so verbosely. In this case explainations and
reasoning are the important part...not necissarily the code itself. As
a coding standard I try to make my code itself do the explaining and
not make use of cryptic style that HAS to be commented.

One question I do have for the experts: In the copy constructor I
originally had new T[colCount_ * rowCount_] but this caused occasional
crashes (that were actually not so occasional). It is my understanding
that rowCount_ and colCount_ are initialized before the call to new T[]
so that should have worked. Using other's variables instead fixed the
problem for at least 20 runs so I am confident it is fixed. The
question is, why did it not work the first time, my failure or
compilers? If mine, where is my understanding flawed?

CODE: =============== =============== ====

// This class reimplements the dynamic_2d_arra y template created
// by David Maisonave and can be viewed at:
// http://code.axter.com/dynamic_2d_array.h
//
// This version fixes some of the bugs and poor coding practices in the
above
// implementation. I do not recommend doing anything like this, but if
you
// must, and want an example, I can't leave you at the mercy of a coder
that
// comes up with the above and calls himself an expert.
//
// Note: I am also not an expert. There are very likely places where
this could
// be improved. My heart is not into it as I disagree with the very
idea.

#include <cstdlib>
#include <cassert>

// Use typename or class? It doesn't really matter. One could
succesfully
// argue that the primary purpose of this class isn't to contain
classes though.
template< typename T >
class dynamic_2d_arra y
{
public:
dynamic_2d_arra y(size_t rowCount, size_t colCount)
: rowCount_(rowCo unt), colCount_(colCo unt),
storage_(new T[rowCount * colCount])
{
/*
* Changes: more descriptive variable names and a lack for a check
* of size for 0. Allocate any time. The reason for the
* later is simple: the check doesn't help, ignores the user's
* intention, and causes undefined behavior if the class is ever
* used.
*
* The correct way to do what the original code does, making sure
this
* class isn't used with 0 bounds is:
*/
assert(rowCount > 0 && colCount > 0);
/*
* You could throw an exception instead. All depends on if this
is an
* incorrect use of the class or a runtime error. I'm choosing
incorrect
* use of the class. Seems logical and is easier to implement.
*/
}

dynamic_2d_arra y(const dynamic_2d_arra y & other)
: rowCount_(other .rowCount_), colCount_(other .colCount_),
storage_(new T[other.rowCount_ * other.colCount_])
{
// In this case there is no need for the check at all as the
creation
// of a dynamic_2d_arra y with 0 bounds has been disallowed.
// Assert anyway to advertise the precondition...

assert(other.ro wCount_ > 0 && other.colCount_ > 0 && other.storage_
!= 0);

std::copy(other .storage_,
other.storage_ + (rowCount_ * colCount_),
storage_);
}

~dynamic_2d_arr ay() { delete [] storage_; }

// These definitions hide the fact that you are actually returning
// a pointer to insides. Clients are discouraged from depending on
this fact
// as it is not advertized by the interface. You could change this
at any
// time and not affect well behaved clients. Without these
definitions any
// changes would propegate to all callers.
typedef T* row_iterator;
typedef const T* const_row_itera tor;

// Col iterator is much more complex and not in the original
interface.
// Personally I think the interface is not complete without this
definition
// but am not willing to spend the time implementing it. For one,
operator[]
// would then be totally inappropriate and implementing that operator
is the
// purpose of the original class.

row_iterator operator [] (size_t row)
{
return storage_ + (row * colCount_);
}

const_row_itera tor operator [] (size_t row) const
{
return storage_ + (row * colCount_);
}

// That is the extent of the original dynamic_2d_arra y. Those are
all the
// messages that object can respond to. Not very dynamic. Here are
some
// more messages that will let this class at least be useful in that
it will
// do MORE, not LESS, than a vector or primitive array.

// bounds checking implementations of the above:
row_iterator row(size_t row)
{
if (!(row < rowCount))
{
throw std::out_of_ran ge("Row index out of range");
}

return storage_ + (row * colCount_);
}

const_row_itera tor row(size_t row) const
{
if (!(row > rowCount))
{
throw std::out_of_ran ge("Row index out of range");
}

return storage_ + (row * colCount_);
}

// No way to protect the row iterator from invalid col bounds because
of the
// implementation. Changing row_iterator to a smarter object could
fix that.

// provide sizing knowledge, needed by almost any client:
size_t rowCount() const { return rowCount_; }
size_t colCount() const { return colCount_; }

// provide resizing - make the class live up to its name:
void resize(size_t rowCount, size_t colCount)
{
assert(rowCount > 0 && colCount > 0);

T * temp_storage = new T[rowCount * colCount];
memset(temp_sto rage, 0, sizeof(T) * rowCount * colCount);

size_t rows = std::min(rowCou nt, rowCount_);
size_t cols = std::min(colCou nt, colCount_);

for (size_t i = 0; i < rows; ++i)
{
std::copy(stora ge_ + (i * colCount_),
storage_ + (i * colCount_) + cols,
temp_storage + (i * colCount));
}

// operations that could cause an exception are finished.
// None of the following can. Strong guarantee...
std::swap(stora ge_, temp_storage);

rowCount_ = rowCount;
colCount_ = colCount;

delete [] temp_storage;
}

/*
* The lack of this operator is a major deficiency of the original
class.
* At one point it was hidden under the "protected" scope and I
mentioned that
* this was not the best as the class cannot be overriden anyway.
The
* original was better at that time but has since been "improved" to
not have
* or hide this operator. This means it is implemented by default
and will
* generate undefined behavior the first time it is used and the
objects
* destroyed. */
dynamic_2d_arra y & operator = (dynamic_2d_arr ay other)
{
// Strong exception guarantee...
std::swap(stora ge_, other.storage_) ; // If this is new to you look
closely.
std::swap(rowCo unt_, other.rowCount_ );
std::swap(colCo unt_, other.colCount_ );

/* note that 'other' now owns my old storage and will delete it
when this
* function exits. */
}

// To finish the *advertized* interface of the class as commented in
the
// original we must provide direct access to the data storage for C
functons
// that need it. This is reasonable but dangerous.
// Probably better ways to do this:

T * c_array() { return storage_; } // no reason to pretend we are not
doing
// what we are doing...

// Comments should reflect the inherint danger of calling the above
function
// at the least. Use should be highly discouraged. Better yet,
don't
// implement it at all and/or do something better. Perhapse the more
costly
// but more correct way:
// std::auto_ptr<T > c_array() const;

/*
* other useful interface items that could be implemented:
* * explicit constructor from c array.
* * functional interface to get T object as f() or operator ().
*
* Do not be tempted to implement a non-explicit constructor from a c
array
* or to implement an implicit conversion to T* or T**.
*/

private:
T * storage_;
size_t rowCount_;
size_t colCount_;
};
#include <iostream>

int main()
{
typedef dynamic_2d_arra y<int> int_array;

int_array a1(5, 7);
assert(a1.colCo unt() == 7);
assert(a1.rowCo unt() == 5);

a1[3][5] = 20;
a1[4][6] = 30;

assert(a1[3][5] == 20);
assert(a1[4][6] == 30);

a1.resize(7, 10);

assert(a1[3][5] == 20);
assert(a1[4][6] == 30);

a1.resize(4, 6);

assert(a1[3][5] == 20);

int_array a2(a1);

assert(a1[3][5] == 20);

a1[1][1] = 100;

assert(a1[3][5] == 20);
assert(a1[1][1] == 100);

a2 = a1;

assert(a1[3][5] == 20);
assert(a1[1][1] == 100);
assert(a2[3][5] == 20);
assert(a2[1][1] == 100);

std::cout << "ALL IS WELL WITH THE INTERFACE.\n";

return 0;
}

Jun 4 '06 #15

Noah Roberts wrote:
Axter wrote:
Noah Roberts wrote:
Axter wrote:

> Check out the following link for an example:
> http://code.axter.com/dynamic_2d_array.h

STILL trying to pass that off as an example are you???

If you are going to show people how to implement something like that at
least put a little effort into it.


Still passing yourself as an expert in this topic.
If you think you can do better, then do-so, and post it as an example.


I am not posting this as an "example" as I don't agree with the idea.
I am also not posting it as an expert sample. I am posting this to
show some of the things a programmer should do differently than you did
if they insist on going that way and why your code "example" should not
be considered such.

If anyone believes the following code exhibits bad practice in any way
please speak up. I won't promise to agree but do listen and in cases
such as this I believe correctness is more important than my ego.

Note the use of whitespace!!

I did keep my lines to 80 chars but this "newsreader " may still break
the formatting. Does anyone have a suggestion of some place to put
this for better readability?

I don't normally comment so verbosely. In this case explainations and
reasoning are the important part...not necissarily the code itself. As
a coding standard I try to make my code itself do the explaining and
not make use of cryptic style that HAS to be commented.

One question I do have for the experts: In the copy constructor I
originally had new T[colCount_ * rowCount_] but this caused occasional
crashes (that were actually not so occasional). It is my understanding
that rowCount_ and colCount_ are initialized before the call to new T[]
so that should have worked. Using other's variables instead fixed the
problem for at least 20 runs so I am confident it is fixed. The
question is, why did it not work the first time, my failure or
compilers? If mine, where is my understanding flawed?


The following is the variable members in your class.
private:
T * storage_;
size_t rowCount_;
size_t colCount_;
};

The problem with your code, is that a compiler initialize variables in
the order that the variables are listed in your class, and NOT in the
order that they're listed in the initialize list.
Example:
dynamic_2d_arra y(size_t rowCount, size_t colCount)
: rowCount_(rowCo unt), colCount_(colCo unt),
storage_(new T[rowCount_ * colCount_])

If your class tries to use member variables rowCount_ and colCount_ to
initialize storage_, the row and col variables will have random values,
because they haven't gotten initialized yet.
That's why it's important to list your member variables in the order
you wish them to get initialize.
private:
size_t rowCount_;
size_t colCount_;
T * storage_; //This should be last
};

The order in which you create the initialize list does not make any
difference as far as the order of initialization. What makes the
difference is the order in which you have the member variables declared
in your class.
This is a common beginners mistake.

Your code is not even compilable on a standard C++ compiler.
Your assignment operator is missing a return statement, so it returns
nothing.
And you're missing required header <algorithm>, which is needed for
std::copy and std::swap

There's no need for an assertion in your copy constructor, because if
the count is zero, you're already going to get an assertion in the
first constructor, and more over the std::copy will not copy anything
if either row or col variables are zero.

IMHO, it's a poor choice to put iterators in a class, and not have
begin and end member functions, which can easily be added with one line
of code.

The resize should avoid the memset command for two reasons.
First, it assumes T is a POD type, but you have no code validating it's
a POD type.
And secondly, it's inefficient.
You can set the values to zero when you call new by using the following
method:
T * temp_storage = new T[rowCount * colCount]();

Notice that the above line of code has () at the end.
IAW C++ standards, this will cause a POD type to be initialized to zero
value.
So not only is the above method more efficient to initialize POD type
to zero, but you also get the added benifit that if the type is not a
POD type, it will still be compatible.

Also, by adding the assertion for zero in the resize function, you
remove the ability for the class to clear it's data by performing a
resize(0,0).

In general, the idea of a resize function is good, and I'll probably
add a resize function to my class.
Also the c_array() function is a good idea.
The iterators would have been a good idea, if you would have added the
begin and end function.

I give you much credit for making the effort, which is more then I
thought you would do.
koodoos

Jun 4 '06 #16

Axter wrote:
The problem with your code, is that a compiler initialize variables in
the order that the variables are listed in your class, and NOT in the
order that they're listed in the initialize list.
Yep, the compiler generally warns one about this when you do it.
fixed.
Your code is not even compilable on a standard C++ compiler.
You mean on a C++ compiler that meets the standard in a specific way.

The compiler certainly should have warned me about the return of op= at
the least. I am disappointed it didn't...that is a common mistake.
(and the mistake was mine for not telling it to)

Fixed at any rate.

Obviously I rely a bit much on the compiler to tell me when I make
common errors. One can't keep track of all the little stuff and needs
decent tools to tell you when you've made basic mistakes or omisions.
I guess you could call this using the compiler as a crutch or failure
to understand...on the other hand, I don't feel my time is well spent
worrying about such things and if the tool will do that part of the
thinking for me it gives me more brain room for solving actual
problems.

And yes, I have numerous reference books because I can't memorize the
entire standard nor the libraries I use even on a daily basis. I am
fully aware of ordering, I just assumed I would be warned if I made a
mistake in that area and didn't concern myself with it....I was wrong.
In fact I had assumed so much that I didn't even look when it became a
problem.

But at least it gives you a chance to point out beginner mistakes in my
code. Difference between us is that mine is now fixed ;)
There's no need for an assertion in your copy constructor, because if
the count is zero, you're already going to get an assertion in the
first constructor, and more over the std::copy will not copy anything
if either row or col variables are zero.
That is noted in the code. The assertion is for the programmer.
Preconditions need to be documented. It doesn't hurt anything and an
assert is the best way to provide that documentation.

Besides, such /could/ happen and be caused by some sort of memory issue
- buffer overrun for instance. The assert would catch that rare
condition where without it there is just one more bug that could have
been caught but wasn't.

Nope, "unneccisar y" assertion stays. It's good practice. It is doing
what it is supposed to, informing the programmer what the following
code assumes.

IMHO, it's a poor choice to put iterators in a class, and not have
begin and end member functions, which can easily be added with one line
of code.
Actually no they can't. What happens when you increment one? Right...

I called them iterators as in some ways they meet that definition and
there needs to be a typedef but there is one very important detail I
missed. I will fix it by calling them something else as I don't want
to do the work to implement them correctly.

Yet another reason I wouldn't even do this. Too much to account for,
too much to do...vector already exists and is a pretty darn GOOD wheel.

The resize should avoid the memset command for two reasons.
First, it assumes T is a POD type, but you have no code validating it's
a POD type.
Yes, I did make that assumption in that part of the code. That is poor
practice. Fixed.

Interestingly your code completely omits this initialization.
So not only is the above method more efficient to initialize POD type
to zero
It is actually unlikely to be any different at all...except that it
works with non-POD types.
Also, by adding the assertion for zero in the resize function, you
remove the ability for the class to clear it's data by performing a
resize(0,0).
That is ok because that would be an invalid use of that function. That
function is for resizing, not for clearing...one responsibility. .. I
debated having that assertion in there at all as it is not necissary
for the functioning of the code. However, I saw no major problem (just
inconveniences) with disallowing 0 bounds arrays by design as your
version does so it is something I kept. Allowing it here would be
inconsistant and invalid.

I will grant you though, the concept of not allowing 0 bounded arrays
may be flawed.

I would implement a clear() but at this point it isn't clear what that
would do. Yes, this functionality would probably be expected though
isn't actually necissary since the client can accomplish it with the
functionality that exists without breaking into the class or having to
track extra data this class should be tracking...what ever they think
clear() should do.

What constitutes a clearing would need to be defined and then it could
be implemented. It can't be a 0 bounded array though because that is
invalid at this point. The design would need to change for that.
Also the c_array() function is a good idea.
It is documented in your header but not implemented. I think it is a
bad idea myself and I explain that in the code. But in an effort to
provide the advertised design of dynamic_2d_arra y I added the
functionality against my better judgement....I did that a lot here.
The iterators would have been a good idea, if you would have added the
begin and end function.


Actually, no they are a bad idea...or at least much more involved that
you think. I'm removing the concept.

The necessity of begin()/end() is debatable but it would be consistant
with the STL in that ONE regard.
NEW CODE=========== =======

// This class reimplements the dynamic_2d_arra y template created
// by David Maisonave and can be viewed at:
// http://code.axter.com/dynamic_2d_array.h
//
// This version fixes some of the bugs and poor coding practices in the
above
// implementation. I do not recommend doing anything like this, but if
you
// must, and want an example, I can't leave you at the mercy of a coder
that
// comes up with the above and calls himself an expert.
//
// Note: I am also not an expert. There are very likely places where
this could
// be improved. My heart is not into it as I disagree with the very
idea.

#include <cstdlib>
#include <cassert>
#include <algorithm>

// Use typename or class? It doesn't really matter. One could
succesfully
// argue that the primary purpose of this class isn't to contain
classes though.
template< typename T >
class dynamic_2d_arra y
{
public:
dynamic_2d_arra y(size_t rowCount, size_t colCount)
: rowCount_(rowCo unt), colCount_(colCo unt),
storage_(new T[rowCount * colCount]())
{
/*
* Changes: more descriptive variable names and a lack for a check
* of size for 0. Allocate any time. The reason for the
* later is simple: the check doesn't help, ignores the user's
* intention, and causes undefined behavior if the class is ever
* used.
*
* The correct way to do what the original code does, making sure
this
* class isn't used with 0 bounds is:
*/
assert(rowCount > 0 && colCount > 0);
/*
* You could throw an exception instead. All depends on if this
is an
* incorrect use of the class or a runtime error. I'm choosing
incorrect
* use of the class. Seems logical and is easier to implement.
*/
}

dynamic_2d_arra y(const dynamic_2d_arra y & other)
: rowCount_(other .rowCount_), colCount_(other .colCount_),
storage_(new T[other.rowCount_ * other.colCount_]())
{
// In this case there is no need for the check at all as the
creation
// of a dynamic_2d_arra y with 0 bounds has been disallowed.
// Assert anyway to advertise the precondition...

assert(other.ro wCount_ > 0 && other.colCount_ > 0 && other.storage_
!= 0);

std::copy(other .storage_,
other.storage_ + (rowCount_ * colCount_),
storage_);
}

~dynamic_2d_arr ay() { delete [] storage_; }

// These definitions hide the fact that you are actually returning
// a pointer to insides. Clients are discouraged from depending on
this fact
// as it is not advertized by the interface. You could change this
at any
// time and not affect well behaved clients. Without these
definitions any
// changes would propegate to all callers.
typedef T* row_reference;
typedef const T* const_row_refer ence;

// Col reference is much more complex and not in the original
interface.
// Personally I think the interface is not complete without this
definition
// but am not willing to spend the time implementing it. For one,
operator[]
// would then be totally inappropriate and implementing that operator
is the
// purpose of the original class.

// Why not "iterators" ? Imagine using ++ on one and what you would
get.
// These are not row iterators and any other type would be bad
design.
// I'm too lazy to implement row iterators correctly - just as
complex as
// a col reference would be. That is an exercize left to the reader.

row_reference operator [] (size_t row)
{
return storage_ + (row * colCount_);
}

const_row_refer ence operator [] (size_t row) const
{
return storage_ + (row * colCount_);
}

// That is the extent of the original dynamic_2d_arra y. Those are
all the
// messages that object can respond to. Not very dynamic. Here are
some
// more messages that will let this class at least be useful in that
it will
// do MORE, not LESS, than a vector or primitive array.

// bounds checking implementations of the above:
row_reference row(size_t row)
{
if (!(row < rowCount))
{
throw std::out_of_ran ge("Row index out of range");
}

return storage_ + (row * colCount_);
}

const_row_refer ence row(size_t row) const
{
if (!(row > rowCount))
{
throw std::out_of_ran ge("Row index out of range");
}

return storage_ + (row * colCount_);
}

// No way to protect the row reference from invalid col bounds
because of the
// implementation. Changing row_reference to a smarter object could
fix that
// and allow a true row_iterator.

// provide sizing knowledge, needed by almost any client:
size_t rowCount() const { return rowCount_; }
size_t colCount() const { return colCount_; }

// provide resizing - make the class live up to its name:
void resize(size_t rowCount, size_t colCount)
{
assert(rowCount > 0 && colCount > 0);

T * temp_storage = new T[rowCount * colCount]();

size_t rows = std::min(rowCou nt, rowCount_);
size_t cols = std::min(colCou nt, colCount_);

for (size_t i = 0; i < rows; ++i)
{
std::copy(stora ge_ + (i * colCount_),
storage_ + (i * colCount_) + cols,
temp_storage + (i * colCount));
}

// operations that could cause an exception are finished.
// None of the following can. Strong guarantee...
std::swap(stora ge_, temp_storage);

rowCount_ = rowCount;
colCount_ = colCount;

delete [] temp_storage;
}

/*
* The lack of this operator is a major deficiency of the original
class.
* At one point it was hidden under the "protected" scope and I
mentioned that
* this was not the best as the class cannot be overriden anyway.
The
* original was better at that time but has since been "improved" to
not have
* or hide this operator. This means it is implemented by default
and will
* generate undefined behavior the first time it is used and the
objects
* destroyed. */
dynamic_2d_arra y & operator = (dynamic_2d_arr ay other)
{
// Strong exception guarantee...
std::swap(stora ge_, other.storage_) ; // If this is new to you look
closely.
std::swap(rowCo unt_, other.rowCount_ );
std::swap(colCo unt_, other.colCount_ );

/* note that 'other' now owns my old storage and will delete it
when this
* function exits. */

return *this;
}

// To finish the *advertized* interface of the class as commented in
the
// original we must provide direct access to the data storage for C
functons
// that need it. This is reasonable but dangerous.
// Probably better ways to do this:

T * c_array() { return storage_; } // no reason to pretend we are not
doing
// what we are doing...

// Comments should reflect the inherint danger of calling the above
function
// at the least. Use should be highly discouraged. Better yet,
don't
// implement it at all and/or do something better. Perhapse the more
costly
// but more correct way:
// std::auto_ptr<T > c_array() const;

/*
* other useful interface items that could be implemented:
* * explicit constructor from c array.
* * functional interface to get T object as f() or operator ().
*
* Do not be tempted to implement a non-explicit constructor from a c
array
* or to implement an implicit conversion to T* or T**.
*/

private:
size_t rowCount_;
size_t colCount_;
T * storage_;
};

Jun 5 '06 #17

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
5251
by: | last post by:
Hi all, I've been using C++ for quite a while now and I've come to the point where I need to overload new and delete inorder to track memory and probably some profiling stuff too. I know that discussions of new and delete is a pretty damn involved process but I'll try to stick to the main information I'm looking for currently. I've searched around for about the last too weeks and have read up on new and overloading it to some extent but...
16
3101
by: gorda | last post by:
Hello, I am playing around with operator overloading and inheritence, specifically overloading the + operator in the base class and its derived class. The structure is simple: the base class has two int memebers "dataA", "dataB". The derived class has an additional int member "dataC". I am simply trying to overload the + operator so that 'adding' two objects will sum up the corresponding int members.
2
2070
by: pmatos | last post by:
Hi all, I'm overloading operator<< for a lot of classes. The question is about style. I define in each class header the prototype of the overloading as a friend. Now, where should I define the overloading of operator<<. In the .cc of the respective class or in a file where I am overloading operator<< for all classes? Cheers,
5
2492
by: luca regini | last post by:
I have this code class M { ..... T operator()( size_t x, size_t y ) const { ... Operator overloading A ....} T& operator()( size_t x, size_t y )
2
2265
by: brzozo2 | last post by:
Hello, this program might look abit long, but it's pretty simple and easy to follow. What it does is read from a file, outputs the contents to screen, and then writes them to a different file. It uses map<and heavy overloading. The problem is, the output file differs from input, and for the love of me I can't figure out why ;p #include <iostream> #include <fstream> #include <sstream>
5
3634
by: Jerry Fleming | last post by:
As I am newbie to C++, I am confused by the overloading issues. Everyone says that the four operators can only be overloaded with class member functions instead of global (friend) functions: (), , ->, =. I wonder why there is such a restriction. Some tutorials say that 'new' and 'delete' can only be overloaded with static member functions, others say that all overloading function should be non-static. Then what is the fact, and why? ...
3
3286
by: y-man | last post by:
Hi, I am trying to get an overloaded operator to work inside the class it works on. The situation is something like this: main.cc: #include "object.hh" #include "somefile.hh" object obj, obj2 ;
9
3519
by: sturlamolden | last post by:
Python allows the binding behaviour to be defined for descriptors, using the __set__ and __get__ methods. I think it would be a major advantage if this could be generalized to any object, by allowing the assignment operator (=) to be overloaded. One particular use for this would be to implement "lazy evaluation". For example it would allow us to get rid of all the temporary arrays produced by NumPy. For example, consider the...
2
4445
by: Colonel | last post by:
It seems that the problems have something to do with the overloading of istream operator ">>", but I just can't find the exact problem. // the declaration friend std::istream & operator>(std::istream & in, const Complex & a); // the methods correspond to the friend std::istream & operator>(std::istream & in, const Complex & a) { std::cout << "real: ";
8
2979
by: Wayne Shu | last post by:
Hi everyone, I am reading B.S. 's TC++PL (special edition). When I read chapter 11 Operator Overloading, I have two questions. 1. In subsection 11.2.2 paragraph 1, B.S. wrote "In particular, operator =, operator, operator(), and operator-must be nonstatic member function; this ensures that their first operands will be lvalues". I know that these operators must be nonstatic member functions, but why this ensure their first operands will...
0
9595
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10354
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10097
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9175
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7642
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6867
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5535
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
4313
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3835
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.