C++ pass const char* pointer array to object

Viewed 1836

Dear StackOverFlowers,

I'm having trouble passing a const char* [] to an object. The scenario is as follows.

I have a class UlamScreen which contains a const char* [] with several strings. UlamScreen also contains an object homeScreenMenu.

class UlamScreen {
  const char* homeScreenText[5] = {"EVA dun", "Sabine", "TPU dun", "test Wout", 
  UlamScreenMenu homeScreenMenu;
};

class UlamScreenMenu {    
private:
  const char* _menuText[];

public:
  UlamScreenMenu(const char*[]);
  void drawMenu();
};

I want to pass the const char* [] to UlamScreenMenu so I can use it in a member function called void drawMenu, like this:

void UlamScreenMenu::drawMenu() {
  for (int i = 0; i < menuItems; i++) {
      tft.println(_menuText[i]);
  }
}

I passed it to UlamScreenMenu's constructor like this:

UlamScreen::UlamScreen() : homeScreenMenu(homeScreenText) {

}

UlamScreenMenu::UlamScreenMenu(const char* menuText[], int length) {
  for(int i = 0; i < length; i++) {
    _menuText[i] = menuText[i];
  }
}

I thought this would work, but for some reason, it does not. tft.println(_menuText[i]); used with void drawMenu does not send anything to my tft screen. When I use tft.println(_menuText[i]); from within the UlamScreen class it works perfectly.

Just to be clear, I can use the tft object within the UlamScreenMenu class because other functions like tft.drawRect() are working correctly.

What is wrong with this way of passing the const char* []? Thanks in advance.

1 Answers

In C++, you can't declare a member variable of type const char* x[], since this would denote a flexible array member. Flexible array members are a C-feature allowing the last member of a struct to be an array of varying size (cf., for example, Arrays of unknown size / flexible array members). Having parameters of type const char* x[] in functions, however, is supported and has basically the same meaning as const char** x.

If you stick to a member of type const char**, then you'll have to handle memory management in that class. This means: take care of allocating, deallocating, copying, moving, copy-assigning, and move-assigning objets of that class (cf, for example, the rule of 0/3/5).

If - as suggested in the comments - you use standard library collections, e.g. std::vector, these classes will do all this stuff in a reliable manner for you. See the following example illustrating the difference between both:

Note that the C++-version probably would not even take a const char*[]-parameter but directly a const std::vector<const char*> &x-parameter. But I kept the const char*[]-parameter in the constructor to provide the same interface in both variants:

// Variant 1: "old" C-style:
class Menu {
public:

    Menu(const char* x[], int length) {
        m_x = new const char*[length];
        m_length = length;
        for (int i=0; i<length; i++) {
            m_x[i] = x[i];
        }
    }

    ~Menu() {
        delete[] m_x;
    }

    // TODO: implement copy- and move constructors + copy- and move assignments
    // ...

    void print() {
        for (int i=0; i<m_length; i++) {
            std::cout << m_x[i] << std::endl;
        }
    }

private:
    const char** m_x = nullptr;
    int m_length;
};

#include <vector>

// Variant 2: a C++- way:
class Menu2 {
public:
    Menu2(const char* x[], int length) {
        m_x.assign(x, x+length);
    }

    void print() {
        for (auto s : m_x) {
            std::cout << s << std::endl;
        }
    }

    // Menu2 does not manage memory on its own, hence:
    // No special copy/move - constructors/assignments to be implemented.
    // No special destructor necessary
private:

    std::vector<const char*> m_x;
};

int main() {
    const char* x1[3] = {"one","two","three" };
    const char* x2[2] = {"eins","zwei" };

    // Variant 1
    Menu m1(x1, 3);
    m1.print();

    // Variant 2
    Menu2 m2(x2, 2);
    m2.print();
}
Related