-2

When initializing a pointer to allocate memory in the heap, should we specify the type as a pointer? That is, should it be:

int* ptr = new num;

OR

ptr = new num;

Both seem to work, and I was under the impression that the first method is better, but doing it is causing some issues in my code below.

I have a class with two member variables, both of which should be pointers to a new int on the heap. When the copy constructor is invoked, it should allocate new memory in the heap, but copy the values of the ints to which the pointers of the argument being passed in are pointing.


#include <iostream>
using namespace std;

// Header file
#pragma once

class numPair {
  public:
    int *pa,*pb;
    numPair(int, int);
    numPair(const numPair &);
 };

// Implementation file
#include "Cube.h"
#include <iostream>
using namespace std;

numPair::numPair(int a, int b) {
   cout << "Constructor invoked!" << endl;

   int* pa = new int(a);
   cout << "pa = " << pa << endl;
   cout << "*pa = " << *pa << endl;

   int* pb = new int(b);
   cout << "pb = " << pb << endl;
   cout << "*pb = " << *pb << endl;

}

numPair::numPair(const numPair& other) {
   cout << "Copy invoked!" << endl;
  int* ptr = other.pa;
  cout << "ptr in copy: " << ptr << endl;
}
 
// main.cpp file
#include "Cube.h"
#include <iostream>
using namespace std;

int main() {
  numPair p(15,16);
  cout << "p.pa = " << p.pa << endl;
  numPair q(p);
  numPair *hp = new numPair(23, 42);
  delete hp;
  return 0;
}

The output of the main.cpp file is:

Constructor invoked!
pa = 0x5594991a7280
*pa = 15
pb = 0x5594991a72a0
*pb = 16
p.pa = 0x3
Copy invoked!
ptr in copy: 0x3
Constructor invoked!
pa = 0x5594991a72e0
*pa = 23
pb = 0x5594991a7300
*pb = 42

As can be seen, when the constructor is invoked, the pa and pb are both valid memory addresses to ints holding the values 15 and 16. However, once the constructor has finished executing, if I try to access the value of p.pa, it returns 0x3, instead of the memory address from before. This is, of course, a problem on its own and also leads to issues with the copy constructor.

I've found two ways of fixing this.

  1. The first is to change the definition of the constructor by removing the type of pa and pb. That is changing int* pa = new int(a); to pa = new int(b). Repeat the same for pb. Now, p.pa returns the correct memory address.

  2. The second is by commenting out the initialisation and deletion of hp. That is, removing these lines from the main() function:

numPair *hp = new numPair(23, 42);
delete hp;

I don't really understand why specifying the type of a pointer is causing these errors or, in fact, why the memory addresses for p.pa and p.pb are having errors because of the initialization of another instance of the class, especially when considering the fact that hp is initialized after p.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
anonymous1a
  • 785
  • 1
  • 7
  • 12
  • 2
    `int* pa = new int(a);` This hides the `pa` member variable of the same name. – dxiv Aug 09 '20 at 19:44
  • It has nothing to do with pointers or "heap" memory allocation. It is the same as saying `int n = 42;` vs. `n = 42;`. – juanchopanza Aug 09 '20 at 19:45
  • Could you explain what you mean by its 'hides' the `pa` member variable? Or link me to a resource for understanding this better? – anonymous1a Aug 09 '20 at 19:45
  • 2
    @anonymous1a That line allocates a `new int`, and saves a pointer to it in the local variable `pa` defined in the constructor, which then goes out of scope at the end of the constructor. The member variable `pa` is left unchanged. See for example [Priority between local variable and class attribute in case of name conflict](https://stackoverflow.com/questions/34061864/priority-between-local-variable-and-class-attribute-in-case-of-name-conflict/). – dxiv Aug 09 '20 at 19:46
  • Sorry, I'm new to C++. So, in other words, by including `int*` I'm creating a new variable within the scope of the constructor itself, as opposed to using the member variable of the class? And I guess this happens because the type of `pa` has already been defined in the header file, so specifying the type again makes the compiler believe this is a new variable altogether? – anonymous1a Aug 09 '20 at 19:50
  • Which book are you using to learn C++, @anonymous1a? – Asteroids With Wings Aug 09 '20 at 19:50
  • @AsteroidsWithWings I'm currently going through a CS Fundamentals Specialization from UIUC on Coursera. As for books, my next stop will probably be this: https://runestone.academy/runestone/books/published/cpp4python/index.html – anonymous1a Aug 09 '20 at 19:52
  • 2
    @anonymous1a I added a link in my previous comment with more details. The type of the local `pa` does not matter as far as name resolution goes. You could define a `double pa = 3.14;` instead, and in that case `pa` would also refer to the local (double) variable inside the constructor. – dxiv Aug 09 '20 at 19:53
  • @dxiv So, it's like doing `int n; n = 7` vs. `int n; int n = 7`, with the latter redefining a new variable of the same name? While I think I understand that, what I still don't get is why this issue is solved by removing the initialisation and deletion of `hp` later on. – anonymous1a Aug 09 '20 at 19:55
  • 1
    @anonymous1a Key point here is that the two definitions occur at different scopes. This really should be explained in the textbook you are using. – dxiv Aug 09 '20 at 19:57
  • 1
    You're going to need a proper C++ book. [Here's a starting point.](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Asteroids With Wings Aug 09 '20 at 19:58
  • Thanks! You've both been really helpful — and yeah, I plan on reading a textbook after I finish the Coursera Spec as a 'basic introduction' first. – anonymous1a Aug 09 '20 at 20:10
  • Does this answer your question? [In C++, what is the scope resolution ("order of precedence") for shadowed variable names?](https://stackoverflow.com/questions/2804880/in-c-what-is-the-scope-resolution-order-of-precedence-for-shadowed-variab) – rsjaffe Aug 09 '20 at 20:12

1 Answers1

0

The short answer is that you are misunderstanding the distinction between locally scoped variables and class members. One way to think about this is that you only need to tell your program the type of an identifier once.

In your class definition you declare two pointers using the type int (int *pa,*pb), but in your constructor numPair::numPair(int a, int b) you are creating two new pointer declarations (int* pa and int* pb) that only exist within the constructor. From within your class constructor, you can reference its members by name without declaring a type every time, since you already declared them in the class definition. This is why your solution #1 works for you.

Additionally, in your original code, you are leaking the memory allocated on the heap for the values because you do not call delete on those pointers.

wideize
  • 143
  • 3
  • 7
  • 1
    Thanks! That helps a lot! As for leaking memory, the class also has a custom destructor that calls delete o the pointers, but that wasn't relevant to the issue so I didn't include it here. – anonymous1a Aug 13 '20 at 05:51