c++memory-managementc++20const-referencepass-by-const-reference

C++20 : Memory allocation of literal initialization of const references


I am trying to optimize for speed of execution a piece of code using the factory design pattern.

The factory will produce many objects of a class having some members that are constant throughtout the execution of the program, and some members that are not. I always initialize the constant members with literals.

My question is, how is the memory of the constant members going to be managed? Can the compiler optimize the code so that this memory is not allocated/deallocated each time I create/destroy a new object?

Ideally I would like the literals to reside on a fixed piece of memory and only the reference to this memory be given to each new object. I would like to avoid using global variables.

class Test {
private:
  const std::string &name_;
  int value_;
public:
  Test(const std::string &name, int value) : name_(name), value_(value) {}
};

int main() {
  Test test1("A", 1);
  Test test2("B", 2);
  Test test3("B", 3);
  Test test4("A", 4);
  Test test5("B", 5);
  // etc ...
}

PS1. notice that in the code snippet the factory pattern implementation is not shown.

PS2. Assume GCC 11.2.0 on Linux


Solution

  • The easiest way to get what you want is just to make name_ a const char *:

    class Test {
    private:
      const char *const name_;
      int value_;
    public:
      Test(const char *name, int value) : name_(name), value_(value) {}
    };
    
    int main() {
      Test test1("A", 1);
      Test test2("B", 2);
      Test test3("B", 3);
      Test test4("A", 4);
      Test test5("B", 5);
      // etc ...
    }
    

    That works because the compiler will optimize strings in memory, so that in this case you don't need multiple copies of "A" in memory.

    Unfortunately, the code you supply will not work, and leaves a dangling reference. The reason is that the rules for when to extend temporary object lifetimes extend when the reference is bound to a temporary materialization of an object. However, in your code, the temporary std::string is first bound to name, and then name is bound to name_. So the temporarily materialized std::string will be destroyed at the semicolon.

    One downside to using a const char * is that you will lose length information in the event that you want to embed NUL characters ('\0') in your string. A potential work around would be to take the length of the string as a template argument, as in:

    class Test {
    private:
      const char *const name_;
      const std::size_t size_;
      int value_;
    public:
      template<std::size_t N>
      Test(const char name[N], int value)
        : name_(name), size_(N-1), value_(value) {}
    };