RGS
RGS

Reputation: 21

How to dynamically allocate memory for nested pointer struct?

I am trying to dynamically allocate memory for a nested struct that happens to be a pointer. I have written some made-up code below to try and illustrate my problem.

These two structs are found in two separate header files, also the code is under one namespace.

Tiles.h

struct Tiles
{
    int* m_noOfSections;
    int* m_noOfTilesInSecs;
    char* m_TileName;
};
// functions omitted

Flooring.h

struct Flooring
{
    Tiles* m_Tiles;
    int m_noOfTiles;
    char* m_FlooringName;
};
void read(Tiles&);

I am working on a function definition for Flooring.h where I have to dynamically allocate an array of Tiles, the size of the array is determined earlier on in this function from user input.

I've tried using the following code but ran into issues:

Flooring.cpp

void read(Flooring&Flr) 
{
      Tiles* tiles;
      tiles = new Tiles[Flr.m_noOfTiles];

      for (int i = 0; i < Flr.m_noOfTiles; i++) {
          cout << i + 1 << ": ";
          read(tiles[i]);
      }
}

Note: The read(tiles[i]); [declaration: void read(Tiles&)] function assigns values to the data members of Tiles. I have tested the functions in the Tiles files and they are working as intended. So I have not included the code for those. I believe the issue lies in my Flooring.cpp implementation file.

My expectation is that that the above read function would assign values to: tiles[i].m_noOfSections, tiles[i].m_noOfTilesinSecs, tiles[i].m_tileName

One of the issues is that I do not have a chance to input tileName, when running the code it skips the part where I would normally input a tileName.

The output would be as follows:

Enter the number of sets of tiles: Enter number of sections: [user is able 
    to input value here, but not before when asked to enter the number of the set of tiles]

Tiles.cpp

void read(char* tileName)
{
    cout << "Enter tile name: ";
    read(tileName, 15, "error message") ;
}

The function definition for the read function with three parameters can be found below. This function was pre-defined in this assignment. I have also reviewed the function and I don't see any problems with it, but I will post it regardless if it helps.

void read(char* str, int len, const char* errorMessage)
{
      bool ok;
      do 
      {
         ok = true;
         cin.getline(str, len+1, '\n');
         if (cin.fail()) {
            cin.clear();
            cin.ignore(1000, '\n');
            ok = false;
         }
      }while(!ok && cout << errorMessage);
   }

I hope that is enough information, apologies if my formatting isn't adequate, or if my terminology isn't appropriate, I am still quite new to all sorts of programming. Please do let me know if I have forgotten to include some information.

Upvotes: 2

Views: 502

Answers (2)

JeJo
JeJo

Reputation: 32722

I would suggest using std::vector and std::string, by which the storage of the elements will be managed automatically and thereby manual memory allocations can be avoided and you can concentrate on the implementations.

That means, have a good start with the followings:

#include <iostream>
#include <vector>  // std::vector
#include <string>  // std::string

struct Tiles
{
    std::vector<int> m_noOfSections;
    std::vector<int> m_noOfTilesInSecs;
    std::string m_TileName;
};

struct Flooring 
{
    std::vector<Tiles> m_Tiles;
    // int m_noOfTiles; // --> obsolete, as you can get the size by calling `m_Tiles.size()`
    std::string m_FlooringName;
};

As a side note, in Flooring(maybe also in Tiles) it does look like, you want to map the Tiles's name to a group of Tiles. As per requirements have a look at other data structure like std::pair, std::map, std::multimap, std::unordered_map, std::unordered_multimap what standard library provides for this scenario.

Upvotes: 2

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14589

new expression doesn't assign anything, it only value-initializes, if you offer those values, or calls constructor and passes arguments to it.

tiles = new Tiles[Flr.m_noOfTiles];

creates an array of Flr.m_noOfTiles class Tiles with garbage non-nullptr pointers and values. Memory for underlings are not initialized. It could be done, by offering initializer list.

tiles = new Tiles[Flr.m_noOfTiles] { value1, value2, value3 };

You have to allocate memory for every pointer. And when it's not needed, deallocate it, in proper order, from most nested to less nested structure.

This might be not a trivial task depending on operations you need and cause quite a hassle in code. It's the reason why C++ have constructors and destructors. So:

  1. is there reason why you don't use RAII? https://en.cppreference.com/w/cpp/language/raii
  2. Is there reason you don't use standard collections, they already implement RAII.
  3. Is there reason why you don't use smart pointers?

Upvotes: 2

Related Questions