critique my string class

Started by
28 comments, last by Nathan Baum 17 years ago
Quote:"const" because it prevents the following absurd use:
	(a = b) = c;


Why? If that's what they want to do, let them do it. Making the return value non-const doesn't cause any behavior that isn't fully expected and reasonable.
Advertisement
OK OK, in *this* instance I was being pedantic. It's just that there's no point in modifying an object twice in this manner anyway.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:
(a = b) = c;


The built-in types allow one to do just that, so making the return value non-const is just following the accepted C++ philosophy "when in doubt, do as the built-ins do".
Quote:There should be two of them, and they will look like this:

Char& operator [](StringLength Pos);
const Char& operator [](StringLength Pos) const;

The first one allows you to write:

String x("abc");
c[1] = 'B';

and the second one allows you to do:

String x("abc");
Char c = x[1];

Without both, you can't do both of these things.


This is wrong. First, you can do both with the first non-const version. Second, the const version should not return a reference.
When I add the const version of operator[], I'm left to wonder what this error means:

cannot convert 'this' pointer from 'const Code::String' to 'Code::String &'


	const Char& String::operator [](StringLength Pos) const	{		if (Pos < 0)			return Text[0];		else if (Pos >= Len)		{			Grow(Pos+2);//Error's here			return Text[Pos];		}		else			return Text[Pos];	}


Thanks, and I did change my destructor
Chad Seibert
Quote:
const Char& String::operator [](StringLength Pos) const{    if (Pos < 0)        return Text[0];    else if (Pos >= Len)    {        Grow(Pos+2);//Error's here	return Text[Pos];    }    else        return Text[Pos];}
You're trying to modify the String object's state inside a function that is declared constant.

The real problem here though is that you're mucking about with internal state in a simple accessor function, i.e. operator[](). IMO, for a string class, operator[]() should return the specified element (or a reference thereto), no more, no less (except perhaps for range-checking, that is).

First of all, why not make StringLength an unsigned type? That removes the need for the < 0 check, and in any case, returning the first element isn't particularly logical behavior in this case. (All in all, I'd say that your current implementation violates the 'principle of least surprise' in several important respects.)

If the index is > 0 and out of bounds, then you should probably assert or throw an exception.

[Edit: Also, what happens in your current implementation if the string is empty and operator[]() is called with a negative index value?]
Nothing, as of now. StringLength is actually unsigned, so I don't know why I was checking to see if it was negative. I also got rid of grow() in the function.

const Char& String::operator [](StringLength Pos) const{	if (Pos >= Len)	{		//Throw exception here		return Text[0];	}	else		return Text[Pos];}


Thanks for all this great help!
Chad Seibert
Quote:Original post by Chad Seibert
Nothing, as of now. StringLength is actually unsigned, so I don't know why I was checking to see if it was negative. I also got rid of grow() in the function.

const Char& String::operator [](StringLength Pos) const{	if (Pos >= Len)	{		//Throw exception here		return Text[0];	}	else		return Text[Pos];}


Thanks for all this great help!
Chad Seibert


Since Char is likely quite small, I would return a copy rather than a const reference (also, when throwing an exception don't bother writing code after it):
Char String::operator [](StringLength Pos) const{	if (Pos >= Len)	{		throw std::out_of_range("String::operator[](StringLength) const: bounds error");	}	else		return Text[Pos];}

Quote:Original post by Deyja
Quote:There should be two of them, and they will look like this:

Char& operator [](StringLength Pos);
const Char& operator [](StringLength Pos) const;

The first one allows you to write:

String x("abc");
c[1] = 'B';

and the second one allows you to do:

String x("abc");
Char c = x[1];

Without both, you can't do both of these things.


This is wrong. First, you can do both with the first non-const version. Second, the const version should not return a reference.
Okay, in the second example I forgot to declare x as const. I did intend to.

Yes I agree that in this instance I would have made the const version return by value. However in cases where the object is larger, you do quite likely want to return it by const reference.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
I think String::Find would be more useful if it returned the position, rather than requiring you to provide an lvalue to store the position in. If nothing was found, it might return -1.

It's unclear what String::TrimLeading, String::TrimTrailing and String::Trim actually trim, even looking at the source. Users shouldn't have to memorize what characters 0x20 and 0x09 are in the particular character set they happen to be using. ' ' and '\t' is better, but giving the functions an argument for specifying what characters to trim would be best; then you could do FileName.TrimTrailing("\").

I don't know why you've got this static String::Length function when there's already a perfectly good function for finding the length of a string: strlen.

It doesn't look like your operators need to be friends.

The implementation of the comparison operators claims Compare might sometimes return -1, but since StringLength is unsigned that is somewhat unlikely.

I don't know why you would need to have typedefs for Bool and Void.

Your implementations of Optimize and Grow are all wrong. Instead of allocating new memory and then immediately forgetting about it, you should be copying the old data into the new array. NewArray = OldArray does not do that.

This topic is closed to new replies.

Advertisement