Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > AnyClass

Reply
Thread Tools

AnyClass

 
 
JKop
Guest
Posts: n/a
 
      10-12-2004
I've written two files:

anyclass.cpp
anyclass.hpp


Within is the definition of the class "AnyClass". It's used for testing
purposes and for playing around, having some fun.

What the class does is (optionally) print text whenever an object of it is
created, destroyed, assigned to.

The constructor works as so:

AnyClass blah("blah");


You supply it with a name. This name will be copied into its internal
storage. All text which is printed will refer to the object by name. If you
do the following:

AnyClass poo = blah;

Then the object "poo" will have the name "Copy of blah".


Here's its public members:

object_counter : a static read-only variable of type "unsigned" which holds
the amount of objects of "AnyClass" currently in existence.

GetName() : returns a pointer to the string which contains the name of the
object.

to_play_with : just a non-const member variable of type "unsigned" to play
around with.


Here's an example usage:


#include "anyclass.hpp"

int main()
{
AnyClass const &blah = AnyClass("no-name");

std::cout << "\nHaHa\n";
}


This program will output the following:

AnyClass Constructor for: no-name

HaHa
AnyClass Destructor for: no-name


Which shows that the temporary in the above is valid until the end of the
function.

If you don't want the class to print out at all, then do the following:

#define ANYCLASS_NO_OUTPUT
#include "anyclass.hpp"


but unfortunately you'll also have to edit the ".cpp" file also to achieve
this.


Here it is. . .

//anyclass.hpp

#ifndef INCLUDE_ANYCLASS_HPP
#define INCLUDE_ANYCLASS_HPP

#ifndef ANYCLASS_NO_OUTPUT
#include <iostream>
#endif

#include <cstddef>
#include <cstring>

class AnyClass
{
public:

static unsigned const &object_counter;

unsigned to_play_with;


const char* GetName() const
{
return name;
}


private:

static unsigned object_counter_prv;
char* name;

public:

AnyClass(const char* const in_name, unsigned const in_to_play_with = 0)
: to_play_with(in_to_play_with)
{

std::size_t length = std::strlen( in_name );

name = new char[length += 1];

memcpy(name, in_name, length);

++object_counter_prv;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Constructor for: " << name <<
'\n';
#endif

}


AnyClass(AnyClass const &original) : to_play_with
(original.to_play_with)
{
std::size_t length = std::strlen( original.name );

name = new char[length += 9]; //9 = 8 + 1 (1 for the null
character)

memcpy( name, "Copy of " , 8 ); // . . .waste of a null
character

memcpy( &name[8], original.name, length -= 9 ); //Take the 9
back off

name[length += 8] = '\0';

++object_counter_prv;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << "AnyClass Copy Constructor for: " << name << '\n';
#endif
}

AnyClass& operator=(AnyClass const &other)
{
//NB: There is no name change whatsoever

to_play_with = other.to_play_with;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Assignment Operator: " << name << "
= " << other.name << '\n';
#endif

return *this;
}

~AnyClass()
{
#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Destructor for: " << name <<
'\n';
#endif

delete [] name;
--object_counter_prv;
}
};


#endif



//anyclass.cpp

#include "anyclass.hpp"

unsigned AnyClass:bject_counter_prv = 0;
unsigned const &AnyClass:bject_counter = AnyClass:bject_counter_prv;




Any comments, questions, suggestions welcomed.


-JKop
 
Reply With Quote
 
 
 
 
David Harmon
Guest
Posts: n/a
 
      10-12-2004
On Tue, 12 Oct 2004 13:34:44 GMT in comp.lang.c++, JKop <(E-Mail Removed)>
wrote,
> std::size_t length = std::strlen( original.name );
>
> name = new char[length += 9]; //9 = 8 + 1 (1 for the null
>character)
>
> memcpy( name, "Copy of " , 8 ); // . . .waste of a null
>character
>
> memcpy( &name[8], original.name, length -= 9 ); //Take the 9
>back off


In my opinion, you should use std::string and omit all that error-prone
char* hacking.

 
Reply With Quote
 
 
 
 
JKop
Guest
Posts: n/a
 
      10-13-2004
David Harmon posted:

> On Tue, 12 Oct 2004 13:34:44 GMT in comp.lang.c++, JKop
> <(E-Mail Removed)> wrote,
>> std::size_t length = std::strlen( original.name );
>>
>> name = new char[length += 9]; //9 = 8 + 1 (1 for the
>> null character)
>>
>> memcpy( name, "Copy of " , 8 ); // . . .waste of a null
>> character
>>
>> memcpy( &name[8], original.name, length -= 9 ); //Take
>> the 9 back off

>
> In my opinion, you should use std::string and omit all that error-prone
> char* hacking.


I strongly disagree.

There is nothing error-prone about the above, nor is there any "hacking".
It's just good ol' C++.

In my own opinion, it's a disgrace to use "std::string" and the like for
simple uses like the above. Does no-one believe in efficiency anymore?

I will although admit that it takes a bit of thinking to make sure you put
in the right array indexes, eg. "&name[8]", but I actually enjoy that and I
end up with much more efficient code.

I'll say that there are however great uses for "std::string". I use in when
I'm doing *extreme* string manipulation, especially where I would have to
resize the buffer were I to do it myself. In the cases, it's preferrable to
use simple "std::string" members than to spend an hour messing with chars.


-JKop
 
Reply With Quote
 
Jerry Coffin
Guest
Posts: n/a
 
      10-13-2004
JKop <(E-Mail Removed)> wrote in message news:<Df5bd.33286$(E-Mail Removed)>...
> David Harmon posted:


[ ... ]

> > In my opinion, you should use std::string and omit all that error-prone
> > char* hacking.

>
> I strongly disagree.
>
> There is nothing error-prone about the above, nor is there any "hacking".
> It's just good ol' C++.
>
> In my own opinion, it's a disgrace to use "std::string" and the like for
> simple uses like the above. Does no-one believe in efficiency anymore?


Quite a few of us do -- and we realize (for example) that doing the
job with std::string will often be substantially MORE efficient than
using C string functions.

Just for example, your code contains a call to strlen, which is an
O(N) function since it has to search through the entire string to find
the end. By contrast, an std::string object normally stores the
current length as a member, meaning that std::string::length() is a
trivial O(1) function.

As such, your code's speed depends (heavily) on the length of
original.name. An implementation using std::string instead can remove
this depency, and retain essentially the same (or better) efficiency
in other areas as well. There will inevitably be a length of
original.name beyond which std::string is more efficient than your
code -- and that length may easily be 0.

> I will although admit that it takes a bit of thinking to make sure you put
> in the right array indexes, eg. "&name[8]", but I actually enjoy that and I
> end up with much more efficient code.


The name for that kind of code is "fragile." If you insist on writing
something like this, a bare minimum of decency would be code something
like:

static char cpy[] = "Copy of ";
static size_t len = sizeof(cpy);

memcpy(name, cpy, len-1);
memcpy(name+len-1, original.name, length-=len);

This way, you don't have a "magic" number like 8 in the code, instead
using a value whose derivation is obvious. Better still, if (for
example) somebody decided to translate the code to their choice of
languages other than English, where "copy of" would almost certainly
be a different size, the size would automatically change with the
change in string length.

IMO, all of this remains essentially pointless: given a halfway
reasonable implementation of the standard library, I'd expect
std::string to be right in the same general ballpark for speed as
doing this manually. To test this theory, I created a modified version
of your class using std::string:

//anyclass.hpp

#ifndef INCLUDE_ANYCLASS_HPP
#define INCLUDE_ANYCLASS_HPP

#ifndef ANYCLASS_NO_OUTPUT
#include <iostream>
#endif

#include <string>

class AnyClass
{
public:
static unsigned const &object_counter;

unsigned to_play_with;

std::string const &GetName() const
{
return name;
}


private:

static unsigned object_counter_prv;
std::string name;

public:

AnyClass(std::string const &in_name, unsigned in_to_play_with =
0)
: to_play_with(in_to_play_with), name(in_name)
{
++object_counter_prv;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Constructor for: " << name <<
'\n';
#endif

}


AnyClass(AnyClass const &original) :
to_play_with(original.to_play_with)
{
name = std::string("Copy of ") + original.name;
++object_counter_prv;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << "AnyClass Copy Constructor for: " << name <<
'\n';
#endif
}

AnyClass& operator=(AnyClass const &other)
{
//NB: There is no name change whatsoever

to_play_with = other.to_play_with;

#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Assignment Operator: " <<
name << " = " << other.name << '\n';
#endif

return *this;
}

~AnyClass()
{
#ifndef ANYCLASS_NO_OUTPUT
std::cout << " AnyClass Destructor for: " << name <<
'\n';
#endif
--object_counter_prv;
}
};

#endif

and then I whipped together a quick test harness:

#define ANYCLASS_NO_OUTPUT

#include <time.h>

#ifdef USESTRING
#include "anyclass.hpp"
#else
#include "anyclass1.hpp"
#endif

#include <iostream>
int main() {

const int num = 5000;

AnyClass *x[num];

clock_t start = clock();
x[0] = new AnyClass("X");
for (int i=1; i<num; i++)
x[i] = new AnyClass(*x[i-1]);
clock_t overall = clock()-start;

#ifdef USESTRING
std::cout << "length: " << x[num-1]->GetName().length();
#else
std::cout << "length: " << strlen(x[num-1]->GetName());
#endif

std::cout << "\nTime: " << overall/(double)CLOCKS_PER_SEC <<
" seconds\n";
return 0;
}

I compiled the code with VC++ 7.1 using:

cl /O2b2 /G7ry any.cpp anyclass.cpp

and:

cl /DUSESTRING /O2b2 /G7ry any.cpp anyclass.cpp

and ran both versions. On my particular machine, both versions claimed
to run in .312 seconds. It's possible (probable, in reality) that if
you ran them often enough that you'd at least ocassionally see a
difference in speed (on the order of a single clock tick worth, I'd
guess). With a lot of runs and careful statistical analysis, you might
even even find a statistically significant difference between them --
but without more testing, it's hard to even guess which one would be
likely to win -- std::string might be about as likely to be faster as
slower. In any case, I think that's more or less irrelevant: there's
no real room for question that the code using std::string is a LOT
simpler, and the code using raw char *'s clearly is not a LOT faster,
which is what would be needed to justify its own existence.

I'd also note that using raw pointers makes exception safety
considerably more difficult to provide.

--
Later,
Jerry.

The universe is a figment of its own imagination.
 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      10-13-2004
Jerry Coffin posted:

> JKop <(E-Mail Removed)> wrote in message
> news:<Df5bd.33286$(E-Mail Removed)>...
>> David Harmon posted:

>
> [ ... ]
>
>> > In my opinion, you should use std::string and omit all that
>> > error-prone char* hacking.

>>
>> I strongly disagree.
>>
>> There is nothing error-prone about the above, nor is there any
>> "hacking". It's just good ol' C++.
>>
>> In my own opinion, it's a disgrace to use "std::string" and the like
>> for simple uses like the above. Does no-one believe in efficiency
>> anymore?

>
> Quite a few of us do -- and we realize (for example) that doing the
> job with std::string will often be substantially MORE efficient than
> using C string functions.
>
> Just for example, your code contains a call to strlen, which is an
> O(N) function since it has to search through the entire string to find
> the end. By contrast, an std::string object normally stores the
> current length as a member, meaning that std::string::length() is a
> trivial O(1) function.
>
> As such, your code's speed depends (heavily) on the length of
> original.name. An implementation using std::string instead can remove
> this depency, and retain essentially the same (or better) efficiency
> in other areas as well. There will inevitably be a length of
> original.name beyond which std::string is more efficient than your
> code -- and that length may easily be 0.
>
>> I will although admit that it takes a bit of thinking to make sure you
>> put in the right array indexes, eg. "&name[8]", but I actually enjoy
>> that and I end up with much more efficient code.

>
> The name for that kind of code is "fragile." If you insist on writing
> something like this, a bare minimum of decency would be code something
> like:
>
> static char cpy[] = "Copy of ";
> static size_t len = sizeof(cpy);
>
> memcpy(name, cpy, len-1);
> memcpy(name+len-1, original.name, length-=len);
>
> This way, you don't have a "magic" number like 8 in the code, instead
> using a value whose derivation is obvious. Better still, if (for
> example) somebody decided to translate the code to their choice of
> languages other than English, where "copy of" would almost certainly
> be a different size, the size would automatically change with the
> change in string length.
>
> IMO, all of this remains essentially pointless: given a halfway
> reasonable implementation of the standard library, I'd expect
> std::string to be right in the same general ballpark for speed as
> doing this manually. To test this theory, I created a modified version
> of your class using std::string:
>
> //anyclass.hpp
>
> #ifndef INCLUDE_ANYCLASS_HPP
> #define INCLUDE_ANYCLASS_HPP
>
> #ifndef ANYCLASS_NO_OUTPUT
> #include <iostream>
> #endif
>
> #include <string>
>
> class AnyClass
> {
> public:
> static unsigned const &object_counter;
>
> unsigned to_play_with;
>
> std::string const &GetName() const
> {
> return name;
> }
>
>
> private:
>
> static unsigned object_counter_prv;
> std::string name;
>
> public:
>
> AnyClass(std::string const &in_name, unsigned in_to_play_with =
> 0)
> : to_play_with(in_to_play_with), name(in_name)
> {
> ++object_counter_prv;
>
> #ifndef ANYCLASS_NO_OUTPUT
> std::cout << " AnyClass Constructor for: " << name <<
> '\n';
> #endif
>
> }
>
>
> AnyClass(AnyClass const &original) :
> to_play_with(original.to_play_with)
> {
> name = std::string("Copy of ") + original.name;
> ++object_counter_prv;
>
> #ifndef ANYCLASS_NO_OUTPUT
> std::cout << "AnyClass Copy Constructor for: " << name <<
> '\n';
> #endif
> }
>
> AnyClass& operator=(AnyClass const &other)
> {
> //NB: There is no name change whatsoever
>
> to_play_with = other.to_play_with;
>
> #ifndef ANYCLASS_NO_OUTPUT
> std::cout << " AnyClass Assignment Operator: " <<
> name << " = " << other.name << '\n';
> #endif
>
> return *this;
> }
>
> ~AnyClass()
> {
> #ifndef ANYCLASS_NO_OUTPUT
> std::cout << " AnyClass Destructor for: " << name <<
> '\n';
> #endif
> --object_counter_prv;
> }
> };
>
> #endif
>
> and then I whipped together a quick test harness:
>
> #define ANYCLASS_NO_OUTPUT
>
> #include <time.h>
>
> #ifdef USESTRING
> #include "anyclass.hpp"
> #else
> #include "anyclass1.hpp"
> #endif
>
> #include <iostream>
> int main() {
>
> const int num = 5000;
>
> AnyClass *x[num];
>
> clock_t start = clock();
> x[0] = new AnyClass("X");
> for (int i=1; i<num; i++)
> x[i] = new AnyClass(*x[i-1]);
> clock_t overall = clock()-start;
>
> #ifdef USESTRING
> std::cout << "length: " << x[num-1]->GetName().length();
> #else
> std::cout << "length: " << strlen(x[num-1]->GetName());
> #endif
>
> std::cout << "\nTime: " << overall/(double)CLOCKS_PER_SEC <<
> " seconds\n";
> return 0;
> }
>
> I compiled the code with VC++ 7.1 using:
>
> cl /O2b2 /G7ry any.cpp anyclass.cpp
>
> and:
>
> cl /DUSESTRING /O2b2 /G7ry any.cpp anyclass.cpp
>
> and ran both versions. On my particular machine, both versions claimed
> to run in .312 seconds. It's possible (probable, in reality) that if
> you ran them often enough that you'd at least ocassionally see a
> difference in speed (on the order of a single clock tick worth, I'd
> guess). With a lot of runs and careful statistical analysis, you might
> even even find a statistically significant difference between them --
> but without more testing, it's hard to even guess which one would be
> likely to win -- std::string might be about as likely to be faster as
> slower. In any case, I think that's more or less irrelevant: there's
> no real room for question that the code using std::string is a LOT
> simpler, and the code using raw char *'s clearly is not a LOT faster,
> which is what would be needed to justify its own existence.
>
> I'd also note that using raw pointers makes exception safety
> considerably more difficult to provide.



I think this newsgroup has the best community of all I've been to!

Thanks for working all that out, Gerry.


You know that template "getcount" (or something like that) that tells you
the length of an array... well I found another use for it:

getcount("Copy of ");






-JKop
 
Reply With Quote
 
Jerry Coffin
Guest
Posts: n/a
 
      10-14-2004
JKop <(E-Mail Removed)> wrote in message news:<3Lgbd.33354$(E-Mail Removed)>...

[ ... ]

> Thanks for working all that out, Gerry.


You're certainly welcome, but my name is Jerry.

> You know that template "getcount" (or something like that) that tells you
> the length of an array... well I found another use for it:
>
> getcount("Copy of ");


Actually, if you insist on using memcpy (or memmove, etc.) that's
_not_ what you really want. The argument to memcpy specifies a number
of _bytes_ to move, _not_ the number of elements.

In this particular case, with a string of char's, the two will be the
same because the C and C++ standards specify sizeof(char) is always 1.

Using the number of elements will lead to a problem, however, if the
type of string changes -- the obvious choice being to change it to
something like:

wchar_t cpy[] = L"Copy of";

in which case using sizeof still yields the correct size, but using
the number of elements will typically yield one half or one quarter
the correct size.

The bottom line is pretty simple: memcpy works in bytes, so you need
to specify its arguments in bytes. You'd use the number of elements if
you were using a function (or template) that works in the number of
elements, such as some in the C++ standard libary (e.g. std::fill_n --
std::copy uses iterators to specify the starting and ending points).

--
Later,
Jerry.

The universe is a figment of its own imagination.
 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      10-14-2004
Jerry Coffin posted:

> JKop <(E-Mail Removed)> wrote in message
> news:<3Lgbd.33354$(E-Mail Removed)>...
>
> [ ... ]
>
>> Thanks for working all that out, Gerry.

>
> You're certainly welcome, but my name is Jerry.



Sorry about that, force of habit (I live in Ireland and it's spelled
"Gerry" over here).


>> You know that template "getcount" (or something like that) that tells
>> you the length of an array... well I found another use for it:
>>
>> getcount("Copy of ");

>
> Actually, if you insist on using memcpy (or memmove, etc.) that's
> _not_ what you really want. The argument to memcpy specifies a number
> of _bytes_ to move, _not_ the number of elements.
>
> In this particular case, with a string of char's, the two will be the
> same because the C and C++ standards specify sizeof(char) is always 1.
>
> Using the number of elements will lead to a problem, however, if the
> type of string changes -- the obvious choice being to change it to
> something like:
>
> wchar_t cpy[] = L"Copy of";
>
> in which case using sizeof still yields the correct size, but using
> the number of elements will typically yield one half or one quarter
> the correct size.
>
> The bottom line is pretty simple: memcpy works in bytes, so you need
> to specify its arguments in bytes. You'd use the number of elements if
> you were using a function (or template) that works in the number of
> elements, such as some in the C++ standard libary (e.g. std::fill_n --
> std::copy uses iterators to specify the starting and ending points).


memcpy( y, x, getcount(array) * sizeof(array[0]) );


-JKop
 
Reply With Quote
 
David Rubin
Guest
Posts: n/a
 
      10-15-2004
http://www.velocityreviews.com/forums/(E-Mail Removed) (Jerry Coffin) wrote in message news:<(E-Mail Removed) om>...
> JKop <(E-Mail Removed)> wrote in message news:<Df5bd.33286$(E-Mail Removed)>...
> > David Harmon posted:



[snip]

Given some version of class 'AnyClass' with a contructor like

AnyClass(const std::string& name, unsigned value = 0);
or
AnyClass(const char *name, unsigned value = 0);

....

> and then I whipped together a quick test harness:
>
> #define ANYCLASS_NO_OUTPUT
>
> #include <time.h>
>
> #ifdef USESTRING
> #include "anyclass.hpp"
> #else
> #include "anyclass1.hpp"
> #endif
>
> #include <iostream>
> int main() {
>
> const int num = 5000;
>
> AnyClass *x[num];
>
> clock_t start = clock();
> x[0] = new AnyClass("X");
> for (int i=1; i<num; i++)
> x[i] = new AnyClass(*x[i-1]);
> clock_t overall = clock()-start;
>
> #ifdef USESTRING
> std::cout << "length: " << x[num-1]->GetName().length();
> #else
> std::cout << "length: " << strlen(x[num-1]->GetName());
> #endif
>
> std::cout << "\nTime: " << overall/(double)CLOCKS_PER_SEC <<
> " seconds\n";
> return 0;
> }
>
> I compiled the code with VC++ 7.1 using:
>
> cl /O2b2 /G7ry any.cpp anyclass.cpp
>
> and:
>
> cl /DUSESTRING /O2b2 /G7ry any.cpp anyclass.cpp
>
> and ran both versions. On my particular machine, both versions claimed
> to run in .312 seconds. It's possible (probable, in reality) that if
> you ran them often enough that you'd at least ocassionally see a
> difference in speed (on the order of a single clock tick worth, I'd
> guess). With a lot of runs and careful statistical analysis, you might
> even even find a statistically significant difference between them --
> but without more testing, it's hard to even guess which one would be
> likely to win -- std::string might be about as likely to be faster as
> slower. In any case, I think that's more or less irrelevant: there's
> no real room for question that the code using std::string is a LOT
> simpler, and the code using raw char *'s clearly is not a LOT faster,
> which is what would be needed to justify its own existence.


What you want to do is something more like this:

enum {
NUM_ITERATIONS = 5000,
GROW_LENGTH = 131
};
int length = GROW_LENGTH;
AnyClass x("");
for (int i = 0; i < NUM_ITERATIONS; ++i) {
std::string name(length, '#');
const char *NAME = name.c_str();

clock_t start = std::clock();
#ifdef USESTRING
AnyClass a(name);
#else
AnyClass a(NAME);
#endif
AnyClass b(a);
x = a;
clock_t totalTime = std::clock() - start;
std::cout << "length = " << length << ", "
<< "time = " << totalTime
<< std::endl;
length += GROW_LENGTH;
}

Perhaps you will see some more deviation in the results. /david
 
Reply With Quote
 
Jerry Coffin
Guest
Posts: n/a
 
      10-15-2004
JKop <(E-Mail Removed)> wrote in message news:<0Cxbd.33431$(E-Mail Removed)>...

[ ... ]

> > The bottom line is pretty simple: memcpy works in bytes, so you need
> > to specify its arguments in bytes. You'd use the number of elements if
> > you were using a function (or template) that works in the number of
> > elements, such as some in the C++ standard libary (e.g. std::fill_n --
> > std::copy uses iterators to specify the starting and ending points).

>
> memcpy( y, x, getcount(array) * sizeof(array[0]) );


IMO, this is just a roundabout way of getting back to where we started
-- getcount is normally defined something like:

#define getcount(array) (sizeof(array)/sizeof(array[0]))

So you're doing:

sizeof(array)/sizeof(array[0]) * sizeof(array[0])

and the sizeof(array[0])'s cancel, giving sizeof(array).

The bottom line is that if you intend to copy the entire array, you
might as well just specify "sizeof(array)" and be done with it.

--
Later,
Jerry.

The universe is a figment of its own imagination.
 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      10-15-2004

> #define getcount(array) (sizeof(array)/sizeof(array[0]))



#include <cstddef>

template<class T, std::size_t amount_elements>
inline std::size_t getcount( T (&array)[amount_elements] )
{
return amount_elements;
}


-JKop
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments