LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   The right way of C++ class init() function (https://www.linuxquestions.org/questions/programming-9/the-right-way-of-c-class-init-function-4175612201/)

Jerry Mcguire 08-18-2017 09:33 AM

The right way of C++ class init() function
 
Hi all,
Could you teach me the right way of C++ class initialization function, init() ?
Code:

// This class is intended to be reused many times.
class ReusableMessage
{
        size_t len;
        int8_t *buf;
public:
        ReusableMessage( size_t n, int8_t *p )
                : len(n), buf(p)
        {
                init();
        }
        void init()
        {
                if (len && buf) memset( buf, 0, len );
        }
};

// Things look fine ?

class HelloMessage : public ReusableMessage
{
        hello_c_structure_t hello;
public:
        HelloMessage()
                : ReusableMessage( sizeof(hello), (int8_t*)&hello )
        {
                init();
        }
        void init()
        {
                ReusableMessage::init();
                hello.header[0] = 'H'; // e.g. something initialization to the structure.
        }
};

// calling init() in ctor() will call ReusableMessage::init() twice.

So, what is the right thing to do then?

a4z 08-18-2017 09:50 AM

you don not use a init function, you implement the init code direct in the costructor
better, you initialize your members direct.
do not use int8_t *buf and len as dynamic arrays,
use std::vector<int8_t>, it will make you more happy

Jerry Mcguire 08-18-2017 10:20 AM

Cannot use vector<> in my example. I need the pointer to a C structure.

OK. reset() was what I had intended when I first drafted this post.

Code:

// This class is intended to be reused many times.
class ReusableMessage
{
        size_t len;
        int8_t *buf;
        char state;
public:
        ReusableMessage( size_t n, int8_t *p )
                : len(n), buf(p), state(0)
        {
                reset();
        }
        void reset()
        {
                if (len && buf) memset( buf, 0, len );               
        }
};

// Things look fine ?

class HelloMessage : public ReusableMessage
{
        hello_c_structure_t hello;
        char target;
public:
        HelloMessage()
                : ReusableMessage( sizeof(hello), (int8_t*)&hello )
        {
                reset();
        }
        void reset()
        {
                ReusableMessage::reset();
                hello.header[0] = 'H'; // e.g. something initialization to the structure.
        }
};

Let's say I have the need to re-use the HelloMessage object again and again because I want to keep the state variable between uses. The C structure has to be reset every time which is why the memset() is not called in the ctor().

In this case how should reset()s and the ctor()s should be arranged?

pan64 08-18-2017 12:03 PM

you should insert trace messages into your code to see what's happening, or probably you may try a debugger to test it (ddd).
Trace means something like that:
Code:

printf("trace: %s class %s method %s", classname, methodname, checkpoint)
and you can call this function at the beginning of ctor, at the end, and whenever you want (checkpoint=start/end/whatever)

In general you do not need to call reset in HelloMessage, because it will be automatically invoked from the inherited constructor, but you will see that using those trace.

Jerry Mcguire 08-21-2017 08:03 AM

Thank you for hinting. I think I found the right way:
Code:

class Message // name reduced for easy recognition
{
protected:
  size_t len;
  int8_t *buf;
public:
  virtual ~Message() {}
  Message() : len(0), buf(NULL)
  {
    // avoid calling class virtual functions in ctor()
  }
  virtual void reset()
  {
    if( len && buf) memset( buf, 0, len );
  }
};

class HelloMessage : public Message
{
protected:
  hello_c_struct_t hello;
public:
  virtual ~HelloMessage() {}
  HelloMessage()
  {
    // same, do not call reset() here
  }
  virtual void reset()  // <- here
  {
    len = sizeof(hello);
    buf = (int8_t*)&hello;

    Message::reset();

    hello.header[0] = 'H';
  }
};

...

// client code
Message *m = new HelloMessage;
if (m) {
  m->reset();  // call reset from client
  work( m ); // some operation on the object (point to)
  m->reset();  // reset for the next use
  work( m ); // the next use.
  ...
  delete m;
}

One thing I'm not sure about. Must the derived class define the virtual function `virtual void reset()` again or just `void reset()`? See above code marked '<- here'. What is the rule?

pan64 08-21-2017 08:33 AM

you must not implement a virtual function - or at least I think so -, therefore reset of class Message is wrong. But probably I missed something...
Would be nice to see how did you compile it. You ought to use -Wall

ntubski 08-21-2017 12:03 PM

Quote:

Originally Posted by pan64 (Post 5750547)
you must not implement a virtual function

No, that's not the case. You may be conflating "virtual" with "pure virtual". Except that apparently even pure virtual functions can have an implementation:

https://en.wikipedia.org/wiki/Virtua...tual_functions
Quote:

Although pure virtual methods typically have no implementation in the class that declares them, pure virtual methods in C++ are permitted to contain an implementation in their declaring class, providing fallback or default behaviour that a derived class can delegate to, if appropriate.
Quote:

Originally Posted by Jerry Mcguire (Post 5750537)
Must the derived class define the virtual function `virtual void reset()` again or just `void reset()`?

As far as I remember, both ways work. It's just a question of style.

Jerry Mcguire 08-21-2017 12:09 PM

@Pan64 I don't get your reply or why. Please give more details.

Jerry Mcguire 08-21-2017 12:39 PM

Many web search results say that virtual-ness is propagated to the derived class. Even cplusplus.com has an example on Polymorphism that the derived class does not declare the virtual function with the 'virtual' keyword in the derived classes.

Anyway, my personal choice would be sticking with the 'virtual' keyword so that when writing the next derived class I would immediately know which function is virtual.

Thank you all. It was a good discussion and knowledge mining session.

sundialsvcs 08-23-2017 01:30 PM

Methods such as init() and reset() (the names are not significant ...) are sometimes used to avoid timing issues, or simply by convention within a particular programming team. Constructors run in a somewhat-peculiar environment in which, sometimes, "things aren't quite set up yet." So, objects sometimes carry their own initialization and clean-up functions that are intended to be called separately, after the constructor has run and before the destructor. They can also be used to "recycle" an object for re-use, resetting it to its initial state. Programmers are obliged to make explicit calls to them. (The case that you illustrate, where the constructor calls the function, is not one that I commonly see.)

dugan 08-23-2017 01:41 PM

Quote:

Originally Posted by sundialsvcs (Post 5751347)
Methods such as init() and [font=courier]reset()[/i] (the names are not significant ...) are sometimes used to avoid timing issues, or simply by convention within a particular programming team. Constructors run in a somewhat-peculiar environment in which, sometimes, "things aren't quite set up yet." So, objects sometimes carry their own initialization and clean-up functions that are intended to be called separately, after the constructor has run and before the destructor. They can also be used to "recycle" an object for re-use, resetting it to its initial state. Programmers are obliged to make explicit calls to them. (The case that you illustrate, where the constructor calls the function, is not one that I commonly see.)

That's an antipattern.

It results in code that's not exception-safe. If you have code like that, you can't use exception-handling. If you do, you'll get resource leaks.

It's common because a lot of C++ code (Qt, for example) was designed back when exceptions weren't ready for production use.

sundialsvcs 08-25-2017 08:07 PM

Quote:

Originally Posted by dugan (Post 5751350)
That's an antipattern.

It results in code that's not exception-safe. If you have code like that, you can't use exception-handling. If you do, you'll get resource leaks.

It's common because a lot of C++ code (Qt, for example) was designed back when exceptions weren't ready for production use.

You are correct, and thank you for pointing that out.


All times are GMT -5. The time now is 11:03 PM.