-1

When I needed to allocate memory through a function, most of the time I was using double pointers (when I was experiencing C). However, now I'm learning C++ and I was wondering if there is a way to replace double pointer with a reference (&) or something similar?

See a little example bellow!

struct Matrix {
   int** pMatrix;
   int row, column;
};

int allocate_memory(Matrix** ppMatrix) {
    // allocate memory for ppMatrix
}

int main() {
   Matrix* ptr = nullptr;
   allocate_memory(&ptr);
   return 0;
   /*
      Can we do something like this?
      Matrix obj_matrix;
      allocate_memory(obj_matrix);
      ...
   */
}

Edit: some people have been saying that I should avoid using direct allocation in my c++ program. The thing is I'm not allowed to use containers yet since we didn't learn them on the lesson so.. I'm basically forced to do stuff with pointers, yeah..

  • Please use correct terminology. What you call a _"link"_ is generally known as ***reference***. – πάντα ῥεῖ Oct 19 '22 at 17:53
  • You _could_ use references for this. You could also give `Matrix` a constructor instead of using free functions to initialize it. You could also use standard library classes like `std::vector` that handle memory allocation for you. – Nathan Pierson Oct 19 '22 at 17:53
  • 1
    Try to avoid directly allocating memory in your code. Use `std::vector`, `std::list` etc. – Richard Critten Oct 19 '22 at 17:54
  • In C++ this is a common problem that's called "Pointless Use Of Pointers". C++ code should use containers, like `std::vector`, which completely eliminates the need to allocate memory and have pointers everywhere. Just Say No to pointless use of pointers. – Sam Varshavchik Oct 19 '22 at 17:55
  • @πάνταῥεῖ thanks for the correction, I completely forgot this word. Changed. – Рик Plays Oct 19 '22 at 17:56
  • @RichardCritten thanks for the advice, however, we're not enabled to use containers etc. as long as we don't learn them on the lessons, so I'm using pointers for now – Рик Plays Oct 19 '22 at 17:58
  • A C++ class containing raw pointers to allocate memory should only exist as a low level specialized container. Because C++ offers copy/move semantics and that classes have to handle this, while it is not really a beginner's task. Most real world classes rely on standard containers for managing their dynamic subobjects. – Serge Ballesta Oct 19 '22 at 18:00
  • ... And if for any reason you have to use raw pointers to allocated memory, make sure to observe the [rule of five](https://stackoverflow.com/a/4172724/3545273) – Serge Ballesta Oct 19 '22 at 18:03
  • 2
    @РикPlays Tell your instructor to [stop teaching C](https://www.youtube.com/watch?v=YnWhqhNdYyk). Learning "the C way first" is a waste of time and teaches you a lot of bad habits you will eventually have to unlearn if you really want to be effective in C++. – DevSolar Oct 19 '22 at 18:13
  • A better way to make a matrix (that still uses the hard way with pointers to dynamic allocations): https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op Note how there is ONE pointer to ONE allocation, not a mish-mash of pointers pointing to arrays of pointers pointing to arrays. – user4581301 Oct 19 '22 at 18:13
  • 1
    _@РикPlays_ I am with what @DevSolar says, as long you didn't subscribe an ***advanced*** c++ course, there's no need to learn the nuts and brittles of manual memory management. – πάντα ῥεῖ Oct 19 '22 at 18:17

2 Answers2

1

Yes, in this case, you can replace the double-pointer parameter of allocate_memory() with an object reference, eg:

struct Matrix {
   int** pMatrix;
   int row, column;
};

int allocate_memory(Matrix &matrix) {
    // allocate memory for matrix.pMatrix
}

void free_memory(Matrix &matrix) {
    // free memory for matrix.pMatrix
}

int main() {
   Matrix obj_matrix;
   allocate_memory(obj_matrix);
   ...
   free_memory(obj_matrix);
   return 0;
}

Though, in this case, it would be better to define a constructor and destructor instead, eg:

struct Matrix {
   int** pMatrix;
   int row, column;

   Matrix(params...) {
      // allocate memory for pMatrix
   }

   ~Matrix() {
      // free memory for pMatrix
   }

   // also need: Matrix(const Matrix &), Matrix(Matrix&&),
   // and operator=(Matrix) to round out the Rule of 3/5/0...
};

int main() {
   Matrix obj_matrix(params...);
   ...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

As others have pointed out in the comments, you don't need any of this. C++ standard library has correctly and efficiently implemented containers.

Since you can't use them and you have to roll your own, here are some pointers:

  • Take note of single responsibility principle. The class that is responsible with memory management should only be responsible with memory management. That means you create a separate class from Matrix that is responsible with memory management and the Matrix class now doesn't care at all how memory is managed. What do you know C++ standard library already has such a class: std::unique_ptr. I am going to assume that you are not allowed to use that either.

  • in C++ almost always you implement the rule of zero. Since that is out of the window because you can't use standard classes (sigh) you need to implement the rule of three to manually manage your memory. Usually you also implement swap to make this easier.

  • usually for performance reasons a matrix is flattened. That is the underlying memory is a vector of size (n*m) and simple math is used to convert from linear indexing to matrix indexing. This makes allocation simpler so I am going to use that.

  • int is not the best data type for storing size, but then again neither is std::size_t so ... int it is for now.

Somethin like this to get you started (disclaimer: not tested nor compiled):

class VectorMemory
{
     int* data_;
     int size_;

public:
     VectorMemory(int size)
         : data_(new int[size]),
           size_(size)
     {
     }

     VectorMemory& operator=(const VectorMemory& other)
     {
          // Important note: this has no exception guarantee
          // and will leak memory on exception
          // I use this because it is simpler, but keep this in mind

          delete[] data_;
          data_ = new int[other.size_];
          size_ = other.size_;

          for (int i = 0; i < size_; ++i)
              data_[i] = other.data_[i];
     }

     VectorMemory(const VectorMemory& other)
         : data_(new int[other.size_]),
           size_(other.size_)
     {
          for (int i = 0; i < size_; ++i)
             data_[i] = other.data_[i];
     }

     ~VectorMemory()
     {
          delete[] data_;
     }

     int* data_() { return data_; }
     const int* data_() const { retur data_; }

     int size() const { return size_; }
};
class Matrix
{
      MatrixMemory data_;
      int n_, m_;

public:
      Matrix(int n, int m)
         : data{n * m},
           n_{n},
           m_{m}
      {
      }

      int& at(int i, j)
      {
           return data_[i * n_ + j];
      }
}
bolov
  • 72,283
  • 15
  • 145
  • 224