Page 1 of 1

C++ style Programming

Posted: Thu Jul 24, 2008 9:50 pm
by torleif
I have a few basic C++ style questions.

Lets say I have a class:

Code: Select all

class Foo {
  public:
    static foobar(){};     // I use this
    static const Foo(){}; // Other programmers seem to use this
}
I understand what static does, I use it to make functions that access the class but not the object. I only use const for function(input) to ensure it won't change. But why do other programmers use static const?


Irrlicht has typedefs for certain size bytes, for example c8 for 8 bit char, u32 for a 32 bit integer. I understand this for portability, but what one of these is the best to do:

myFile->read(&inChar, sizeof( c8 )); // this
myFile->read(&inChar, 1); // or this

Posted: Thu Jul 24, 2008 10:10 pm
by hybrid
Are you sure that they use static const for functions without defining the return value type? After all, the const is for the return value, so it would only make sense for some reference return values. Making a static method const is not allowed, but also not useful, because it's const by default (it cannot alter an object - it's static). Other uses of const are for const member attributes, which are stored in the class instead of in each object.
Oh, and moved to beginner's section, because it's a C++ question :wink:

Posted: Thu Jul 24, 2008 11:01 pm
by torleif
Yes, I should have mentioned that there are return values

Posted: Fri Jul 25, 2008 2:30 am
by christianclavet
Oh Boy! I have a long way to get there! :?
Is there any good sites for beginners that you'd recommend about this that would like to improve their coding style?

Posted: Fri Jul 25, 2008 7:44 am
by JP
It's generally best to use sizeof(c8), or whatever, when you're specifying the size of something because on different platforms they can be different sizes sometimes, there's no guarantee. So a c8 is a char and a char is 1 byte on windows and it's probably 1 byte everywhere else too but imagine there's a super cool platform called LOLLARSK80RZ!!1 and this platform actually sues 2 bytes to store a char. If you port your code and have used something like myFile->read(&inChar, 1); then it's not going to be very happy about it. sizeof(c8) would automatically stick in the correct byte size for the LOLLARSK80RZ!!1 platform and you'd be problem free.

Posted: Fri Jul 25, 2008 8:05 am
by hybrid
The problem is that the file only provides a 1 byte char, hence reading two byte chars would result in core dumps. It's best to stick to fixed numbers when reading binary files, and to use the official types when writing (so don't use f32 when writing a float, because f32 could as well be a fixed point math class). However, all binary file handling can (and usually will) break at some point.

Re: C++ style Programming

Posted: Fri Jul 25, 2008 8:05 am
by rogerborg
torleif wrote: Lets say I have a class:

Code: Select all

class Foo {
  public:
    static foobar(){};     // I use this
    static const Foo(){}; // Other programmers seem to use this
}
This is not valid C++, so I guess we'd be wasting our time discussing it.

torleif wrote: Irrlicht has typedefs for certain size bytes, for example c8 for 8 bit char, u32 for a 32 bit integer. I understand this for portability, but what one of these is the best to do:

Code: Select all

myFile->read(&inChar, sizeof( c8 )); // this
myFile->read(&inChar, 1); // or this
In principle, if inChar is a value type, then using its size would be safest, since if you change the type of inChar then you don't have to change the sizeof():

Code: Select all

myFile->read(&inChar, sizeof(inChar));

However, in practice, if inChar is of type c8, then sizeof(c8) is fine. If c8 isn't 1 byte, then that's an error in the definition of c8.

And in this specific case, of reading binary data, you want to read an exact size, defined by the file format; if you accidentally read 2 bytes when the value that you want is in 1 byte, then you've screwed up your reader.

So for the purposes of reading formatted binary data, I would actually use:

Code: Select all

myFile->read(&inChar, 1);

Posted: Fri Jul 25, 2008 8:10 am
by JP
hybrid wrote:The problem is that the file only provides a 1 byte char, hence reading two byte chars would result in core dumps. It's best to stick to fixed numbers when reading binary files, and to use the official types when writing
Yeah i had considered that if you were reading a file you wouldn't want to read 2 if it had been written as 1 but i was keeping it simple i guess :lol:

Posted: Sat Jul 26, 2008 6:52 am
by torleif
Thanks for your responses, in the future I will use

Code: Select all

myFile->read(&inChar, 1); // or this
For all my binary input. I guess you should too :P

Sorry about by mistype with my class structure, it really should have been:

Code: Select all

class Foo {
  public:
    static genStruct foobar(){};     // I use this
    static const genStruct Foo(){}; // Other programmers seem to use this
} 
Where genStruct is some structure, object or primitive. (I guess that's where it makes a difference?)

Perhaps the difference static const and const are simply performance issues I shouldn't be concerned with

Posted: Sat Jul 26, 2008 7:48 am
by rogerborg
torleif wrote:

Code: Select all

    static const genStruct Foo(){}; // Other programmers seem to use this
I'll assume that you mean some method other than Foo(), since constructors can't have qualifiers or return types.

Whether this is useful depends on whether genStruct is a value or a reference type. If it's a value type, (e.g. int, or a class/structure returned by value rather than by reference) then there's no point in returning a const version, since it's just dumped on the stack and the caller will be taking a copy of it anyway, and you can't effect their version of it.

However, if you're returning a reference type, i.e. a pointer (e.g. int *) or a reference (e.g. genStruct &) then const is useful, as it stops(1) the caller from modifying the thing that you are returning a reference so.

(1) Except it doesn't really...

Code: Select all

class GotSome
{
public:
    // These methods are static, which means that you don't need an instance
    // of GotSome to call them on.  You can just call GotSome::getSome()
    // and get returned a pointer to some.
    // Note that the pointer/reference are not constant, but the thing that 
    // they point/refer to is const.
    // This means that the caller can't - or rather, that it shouldn't - 
    // change the value of "some" using these methods, they can only get it.
    // Because these are static methods, the methods themselves can't be
    // declared const.  This would be meaningless, since they don't have
    // an object to be called on.
    static const int * getSome(void) { return &some; }
    static const int & getSomeMore(void) { return some; }
    
    // These are instance versions of the above methods.  That means that
    // they are not declared static, so they require an instance of GotSome.
    // You can't call GotSome::getSomeInstance().  Instead, you need to do (e.g.):
    // GotSome aSome;
    // const int * something = aSome.getSomeInstance().
    //
    // The methods themselves are declared const.  This tells that compiler that
    // calling them won't result in the object being altered; or at least
    // not within the method body.
    const int * getSomeInstance(void) const { return &some; }
    const int & getSomeMoreInstance(void) const { return some; }

private:
    static int some;
};

// define the static member, and set its initial value.
// Now, because all the methods that access this return a reference to const,
// it shouldn't be possible for this to change.
int GotSome::some = 1066;

int main()
{
    // All as it should be
    const int * theInt = GotSome::getSome();
    (void)printf("%d\n", *theInt);
    // *theInt = 1314; // The compiler won't let us do this, as theInt points to a const int.

    // Unfortunately, const is more a guidelines than a rule, and the caller can
    // just brutally cast it away.
    GotSome aSome;
    int & theConstIsALie = const_cast<int &>(aSome.getSomeMoreInstance());
    theConstIsALie = 1314;

    // Let's just be absolutely sure that this has changed the class member
    int * liesDamnLies = const_cast<int *>(aSome.getSomeInstance());
    (void)printf("%d\n", *liesDamnLies);

    // Thus ends the lesson on the uses and limitations of const.
    return 0;
}