Passing an array of char to the constructor of a class, to use it later

Thanks for the explanations. I am in a context where the same programmer writes the code on both sides of the call to this constructor or methods, and thus can restrict the usage, which makes not necessary all the checks on the nul pointers or other inventive user tricks. Though I know that testing the preconditions as much as possible is a key to writing robust code.

I should have figured out that it was a destructor : of course, java has this feature, but without this "~" convention.

JiPe38:
which makes not necessary all the checks on the nul pointers...

You always have to check for nullptr after allocating memory dynamically. Otherwise, dereferencing it will quickly lead to a crash.

gfvalvo:
I chose to move it out of the constructor in case dynamic allocation fails.

class MessagePrinter {

private :
    char *what ;

public :
    MessagePrinter () {}

~MessagePrinter() {
      if (what != nullptr) {
        delete what;
      }
    }

bool setString(const char * s) {
      if (what != nullptr) {
        delete what;
      }
      size_t length = strlen(s);

what = new char[length + 1];
      if (what == nullptr) {
        return false;
      }

strcpy(what, s);
      return true;
    }

void goPrint () {
      if (what != nullptr) {
        Serial.println (what);
      }
    }
};

MessagePrinter *message ;

void setup() {
  Serial.begin (115200) ;
  delay(1000);
  message = new MessagePrinter;
  if (message != nullptr) {
    if (message->setString("test")) {
      Serial.print("Success: ");
      message->goPrint () ;
    }
  }
}

void loop() {}

If you're going to use dynamic memory anyway, why not use the String class? It'll handle allocation failures for you as well. I don't see the need to move the allocation out of the constructor, you can't return a bool, but you can still check if what == nullptr afterwards.

I stand by the claim that raw new/delete calls should be avoided whenever possible.
Case in point:

      if (what != nullptr) {
        delete what; // Uh-oh!
      }

(It should be delete[] what.)
(You don't have to check for nullptr first, you can call delete[] nullptr without any problems.)

Pieter

No argument with that. String class seems the way to go given what's known of the application.

Writing with String class was my first approach. I met some nasty problems with that class, like for example the blocking "readString" method, which made me browse here and there and I found that, especially in a real time context on a system with a small memory for the heap and no garbage collector, instantiating many times String objects with different sizes would create such a swiss cheese in the heap that the application would rapidly crash. This is why, following these advice, I went to programming with the old "C" style character array methods, which doesn't presume dynamic allocation. Same for my tasks, which are classes, I will avoid to intensively use "new" - "delete", to avoid the same swiss cheese problem : instead have a pool of persistent tasks, of which perhaps half or more are in a sleeping state until for some reason one is needed and then raised to running state.

gfvalvo:
Rather than saving a pointer to the passed string, the code I posted makes a copy of the string. I did this because you don't know what the user might supply as the string in your original code. If it's a local stack variable, it will cease to exist after the containing function ends and the pointer will no longer be valid. In fact, that memory will probably be used for something else. Run this example and watch what happens:

class MessagePrinter {

private :
    const char *what;
  public :
    MessagePrinter (const char *toPrint){
      what = toPrint;
    }
    void goPrint () {
      Serial.println(what);
    }
};

void createObject();
void printObject();

MessagePrinter * message ;

void setup() {
  Serial.begin (115200) ;
  delay(1000);
  createObject();
  printObject();
  message->goPrint () ;
}

void loop() {}

void createObject(){
  char string[] = "Hello World";
  message = new MessagePrinter(string);
  message->goPrint ();
}

void printObject(){
  char string[] = "Goodbye World";
  Serial.println(string);
  message->goPrint();
}




My code used dynamic allocation to get the memory to store the copy in. That may fail and return nullptr. The user needs to know this. But, you can't return a value from the constructor. So, I made a separate function that returns a bool for pass / fail.

~MessagePrinter() is the destructor. It cleans things up when the object's lifetime expires.

I don’t think that your class declaration will work.
You declare what as char constant thus the compiler won't allow you to change it.

abdelhmimas:
I don’t think that your class declaration will work.
You declare what as char constant thus the compiler won't allow you to change it.

The pointer is not immutable, the characters it points to are.
const char * is a mutable pointer to immutable data, char *const would be an immutable pointer to mutable data.

abdelhmimas:
I don’t think that your class declaration will work.

I tested it, works fine. I almost always test my code suggestions before posting them. If not, I note that it's untested.

@gfvalvo
My bad
Thank you

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.