unique_ptr<char[]> confusion - c++

I have a class in which I would like one of the functions to pass a unique ptr object to a char array. But I'm confused on several on features of unique pointers. I'm aware a destructor is called automatically when there are no more references to the object but is still the same for primitive variables? For instance if I do this, will the memory be deleted?
class A {
private:
public:
A(std::unique_ptr<char[]> data) {
data = nullptr;
}
~A();
};
int main() {
auto data = std::make_unique<char[]>(10);
A a(std::move(data));
return 0;
}
The next question I have is: If I have a private object which I want to point to data, why does this result in a compiler error?
class A {
private:
std::unique_ptr<char[]> internaldata;
public:
A(std::unique_ptr<char[]> data) {
internaldata = data;
}
~A() {
internaldata = nullptr;
}
};
int main() {
auto data = std::make_unique<char[]>(10);
A a(std::move(data));
return 0;
}
However when I call std::move while assigning it, the code compiles fine.
class A {
private:
std::unique_ptr<char[]> internaldata;
public:
A(std::unique_ptr<char[]> data) {
internaldata = std::move(data);
}
~A() {
internaldata = nullptr;
}
};
int main() {
auto data = std::make_unique<char[]>(10);
A a(std::move(data));
return 0;
}
But why do I have to call std::move twice here? Once for passing the argument then the second for assigning? And what exactly occurs in terms of reference count during that process, does a reallocation, copy and deletion occur?
And finally, is it possible to pass data into the smart pointer during the deceleration? Because currently I do it like this:
auto data = std::make_unique<char[]>(10);
char* buf = data.get();
strcpy(buf, "hello\0");
But is it possible to do something along the lines of:
char hellobuffer[] = "hello";
auto data = std::make_unique<char[]>(hellobuffer);
Where data is automatically assigned the correct size needed to store hellobuffer and copies over the data itself?

I'm aware a destructor is called automatically when there are no more references to the object but is still the same for primitive variables?
The destructor is always logically called. However, since things like int and char are trivially-destructible, the compiler understands that nothing should actually get called.
For instance if I do this, will the memory be deleted?
Yes -- the whole point of std::unique_ptr<T> is that your memory is taken care of automatically.
A(std::unique_ptr<char[]> data) {
internaldata = data;
}
That example fails to compile because internaldata = data is calling the copy-assignment operator and copying std::unique_ptr instances is disallowed (hence the unique bit).
And what exactly occurs in terms of reference count during that process, does a reallocation, copy and deletion occur?
There is no reference count -- a std::unique_ptr either refers to something or it is empty. When you std::move from a std::unique_ptr, the moved-from variable becomes empty. If you are looking for a reference-counted pointer type, see std::shared_ptr<T>.
And finally, is it possible to pass data into the smart pointer during the deceleration?
No. For std::make_unique<T[]>, you are only allowed to pass a std::size_t (see overload 2). It should be easy to write a wrapper function for what you are looking for.

Related

Prevent unnecessary copying between large structs

I have huge structs DataFrom and Data (which have different members in reality). Data is Created from DataFrom.
struct DataFrom{
int a = 1;
int b = 2;
};
static DataFrom dataFrom;
struct Data{
int a;
int b;
};
class DataHandler{
public:
static Data getData(const DataFrom& data2){
Data data;
setA(data, data2);
setB(data, data2);
return data;
}
private:
static void setA(Data& dest, const DataFrom& source){
dest.a = source.a;
}
static void setB(Data& dest, const DataFrom& source){
dest.b = source.b;
}
};
int main(){
auto data = DataHandler2::getData(dataFrom); // copy of whole Data structure
// ...
return 0;
}
As Data is huge, in getData function, there is a copying of whole Data structure. Can this be prevented somehow in elegant way?
I had an idea about:
static void getData( Data& data, const DataFrom& data2);
But I would prefer to retrieve data as a return value, not an output parameter.
There are two potential "copy hazards" to address here:
Copy hazard 1: The construction outside getData()
On the first line of main(), where you commented "copy of whole Data structure" - as commenters noted, the structure won't actually be copied, due to the Named Return Value Optimization, or NRVO for short. You can read about it in this nice blog post from a few years back:
Fluent{C++}: Return value optimizations
In a nutshell: The compiler arranges it so that data inside the getData function, when it is called from main(), is actually an alias of data in main.
Copy hazard 2: data and data2
The second "copy scare" is with setA() and setB(). Here you must be more pro-active, since you do have two live, valid structs in the same function - data and data2 within getData(). Indeed, if Data and DataFrom are simply large structs - then you will be doing a lot of copying from data2 to data, the way you wrote your code.
Move semantics to the rescue
If, however, your DataFrom holds a reference to some allocated storage, say, std::vector<int> a instead of int[10000] a - you could move from your DataFrom instead of copying from it - by having getData() with the signature static Data getData(DataFrom&& data2). Read more about moving here:
What is move semantics?
In my example, this would mean you would now use the raw buffer of data2.a for your data - without copying the contents of that buffer anywhere else. But that would mean you can no longer use data2 afterwards, since its a field has been cannibalized, moved from.
... or just be "lazy".
Instead of a move-based approach, you might try something else. Suppose you defined something like this:
class Data {
protected:
DataFrom& source_;
public:
int& a() { return source_.a; }
int& b() { return source_.b; }
public:
Data(DataFrom& source) : source_(source) { }
Data(Data& other) : source_(other.source) { }
// copy constructor?
// assignment operators?
};
Now Data is not a simple struct; it is more of a facade for a DataFrom (and perhaps some other fields and methods). That's a bit less convenient, but the benefit is that you now create a Data with merely a reference to a DataFrom and no copying of anything else. On access, you may need to dereference a pointer.
Other notes:
Your DataHandler is defined as a class, but it looks like it serves as just a namespace. You never instantiate "data handlers". Consider reading:
Why and how should I use namespaces in C++?
My suggestions do not involve any C++17. Move semantics were introduced in C++11, and if you choose the "lazy" approach - that would work even in C++98.
Since you've tagged this with c++17, you can write your code in a way that prevents any copying from taking place (or being able to take place), and if it compiles you will know that statically no copies will be made.
C++17 guarantees copy elision when returned from functions, which ensures no copies take place in certain circumstances. You can ensure this by changing your code so that Data has an = deleted copy-constructor, and changing getData to return a constructed object. If the code compiles correctly at all, you will be sure that no copy occurred (since copying would trigger a compile-error)
#include <iostream>
struct DataFrom{
int a = 1;
int b = 2;
};
static DataFrom dataFrom;
struct Data{
Data() = default;
Data(const Data&) = delete; // No copy
int a;
int b;
};
class DataHandler{
public:
static Data getData(const DataFrom& data2){
// construct it during return
return Data{data2.a, data2.b};
}
private:
static void setA(Data& dest, const DataFrom& source){
dest.a = source.a;
}
static void setB(Data& dest, const DataFrom& source){
dest.b = source.b;
}
};
int main(){
auto data = DataHandler::getData(dataFrom); // copy of whole Data structure
return 0;
}
This will compile without any extra copies -- you can see it here on compiler explorer

Working with shared_ptr returned from another concrete class

I need to have access to an object which is implemented in a different concrete class. So I decided to use std::shared_ptr. I would like to know whether usage of std::shared_ptr is appropriate here, if not please suggest whether I should go with std::weak_ptr. So far I have been using raw pointers but now I decided to use smart pointers in my project, But I'm unable to decide which one should I use here. The following code snippet is analogous to what I'm trying to do in my project.
#include <iostream>
#include <memory>
class data
{
public:
data()
{
std::cout<<"\n data constructor Called"<<std::endl;
}
~data()
{
std::cout<<"\n data destructor Called"<<std::endl;
}
int GetData()
{
return val;
}
void SetData(int & val)
{
this->val = val;
}
private:
int val;
};
class sample
{
public:
sample();
~sample();
void GetShared(std::shared_ptr<data> & arg);
std::shared_ptr<data> sPtr;
};
sample::sample()
{
sPtr = std::make_shared<data>();
}
sample::~sample()
{
}
void sample::GetShared(std::shared_ptr<data> & arg)
{
arg = sPtr;
}
int main()
{
int val = 40;
sample obj;
{
std::shared_ptr<data> temp1;
obj.GetShared(temp1);
temp1->SetData(val);
std::cout<<"\n Data : "<<temp1->GetData()<<std::endl;
} // Just to understand whether pointer gets deleted if temp1 goes out of scope.
{
std::shared_ptr<data> temp2;
obj.GetShared(temp2);
val = 20;
temp2->SetData(val);
std::cout<<"\n Data : "<<temp2->GetData()<<std::endl;
}
return 0;
}
You would use shared_pointer to share ownership of some resource, when you don't have a clear owner of that resource.
Here it would be useful if you don't know if obj goes out of scope before temp1 and temp2 is done with the data. However, in this example it is clear that obj and the data object it holds will outlive the user. In that case you could just as well return a normal pointer, or perhaps a reference to the data.
Using a shared_pointer (or a weak_pointer) doesn't buy you anything, except added complexity.
In your code
sample obj;
{
std::shared_ptr<data> temp1;
obj.GetShared(temp1);
temp1->SetData(val);
std::cout<<"\n Data : "<<temp1->GetData()<<std::endl;
} // Just to understand whether pointer gets deleted if temp1 goes out of scope.
The data will not get deleted, because it is pointing to something that is held by obj, which is still alive.
I think the answer should depend on whether a data object can live after its corresponding sample object died:
If yes, then it should return a std::shared_ptr.
If not, then it should return a std::weak_ptr.
The difference between shared_ptr and weak_ptr is that weak_ptr does not increase the ref count on the object and does prevent the object from being deleted.
This has its pros and cons, if you perform async operations after you obtain the pointer and are unsure whether the object that provided the data has been destroyed then you can use weak_ptr and check if you still have access to the object.
If not then keep it simple and use shared_ptr.
You have two questions you need to ask yourself.
1) Will the calling object that gets the pointer from sample outlive the sample object?
If not then sample should use a std::unique_ptr and return a raw pointer or a reference.
2) If the calling object does outlive the sample object, does it mind if the data object gets destroyed before it does?
If not then return a std::weak_ptr that can be used to test if the data object is still alive before using it.
Otherwise return a std::shared_ptr to guarantee the data object lives as long as the calling object.
Summary:
If the calling object will not outlive the sample object:
class sample
{
std::unique_ptr<data> ptr; // UNIQUE (not shared)
public:
data* GetData() { return ptr.get(); }
};
If the calling object may outlive the sample object but doesn't care if the data object lives as long as it does:
class sample
{
std::shared_ptr<data> ptr;
public:
std::weak_ptr<data> GetData() { return ptr; }
};
If the calling object may outlive the sample object and needs to data object to keep living too:
class sample
{
std::shared_ptr<data> ptr;
public:
std::shared_ptr<data> GetData() { return ptr; }
};

A simple implementation of Smart Pointer Class

In book C++ Primer 13.5.1, it implement a Smart Pointer Class using a Use-Count Class. Their implementation is as follows:
Use-Count Class
// private class for use by HasPtr only
class U_Ptr {
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p): ip(p), use(1) { }
~U_Ptr() { delete ip; }
};
Smart Pointer Class
/*
smart pointer class: takes ownership of the dynamically allocated
object to which it is bound
User code must dynamically allocate an object to initialize a HasPtr
and must not delete that object; the HasPtr class will delete it
*/
class HasPtr {
public:
// HasPtr owns the pointer; p must have been dynamically allocated
HasPtr(int *p, int i)
: ptr(new U_Ptr(p)), val(i) { }
// copy members and increment the use count
HasPtr(const HasPtr &orig)
: ptr(orig.ptr), val(orig.val) { ++ptr->use; }
HasPtr& operator=(const HasPtr&);
// if use count goes to zero, delete the U_Ptr object
~HasPtr() { if (--ptr->use == 0) delete ptr; }
friend ostream& operator<<(ostream&, const HasPtr&);
// copy control and constructors as before
// accessors must change to fetch value from U_Ptr object
int *get_ptr() const { return ptr->ip; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *p) { ptr->ip = p; }
void set_int(int i) { val = i; }
// return or change the value pointed to, so ok for const objects
// Note: *ptr->ip is equivalent to *(ptr->ip)
int get_ptr_val() const { return *ptr->ip; }
void set_ptr_val(int i) { *ptr->ip = i; }
private:
U_Ptr *ptr; // points to use-counted U_Ptr class
int val;
};
Wonder: I am curious about why not simply using a int * to act like the Use-Count Class, just like the int* countPtr; used in the following new Smart Pointer Class:
class T
{
private:
int* countPtr; //
int* p;
int val;
public:
T(){
p = new int();
countPtr = new int();
*countPtr = 1;
val = 0;
}
T(T& t){
p = t.p;
countPtr = t.countPtr;
val = t.val;
*countPtr += 1;
}
T& operator = ( const T& rT){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
p = rT.p;
countPtr = rT.countPtr;
val = rT.val;
*countPtr += 1;
return *this;
}
~T(){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
}
int *get_ptr() const { return p; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *ptr) { p = ptr; }
void set_int(int i) { val = i; }
};
Test: I tested the above Smart Pointer Class using code like the following and it seems working well.
int main()
{
T t1;
T t2(t1);
T t3(t1);
T t4;
t4 = t1;
return 0;
}
Real question: Is this new Smart Pointer Class with simply a int *countPtr sufficient enough? If yes, why bother to use an extra Use-Count Class like in the book? If no, what do I miss?
One property of the original implementation is that the delete is performed, in the control block object, with the original pointer type. This is a partial type erasure. No matter how much the smart pointer objects are copied, with somewhat different types, the original control block remains the same, with delete via the original pointer type.
However, since the original code you show is not templated, one must assume that it is an early example, followed later by similar templated code.
Converting a pointer up in a base class hierarchy, as can happen with copying of a smart pointer, means that delete on the new pointer type is only valid if the statically known new type has a virtual destructor.
For example, std::shared_ptr also deletes (guaranteed) via the original pointer type, unless one explicitly supplies a deleter functor that does something else.
My guess is that the author - whether consciously or subconsciously - is aware that having a separate class is useful in real-world smart pointers, e.g.:
a count of weak pointers (not sure if you'll have heard of them yet - they track an object without extending its lifetime, such that you can try to convert one into a (normal) shared pointer later, but it only works if there's at least one shared pointer to the object around to have kept it alive)
a mutex to make the shared pointer thread safe (though atomic operations may be better when available),
debugging information (e.g. boost::shared_ptr has a #ifdef to include an shared counter id)
virtual dispatch table, used by e.g. boost shared pointers to dispatch to OS-appropriate code (see boost/smart_ptr/detail/sp_counted_base_*.hpp headers)
I don't know the book, but perhaps they'll go on to explain what else might go into U_Ptr....
Your code is equivalent to the standard code reported by the book. However it is worst in some respects:
you need two allocations/deallocations instead of one (two ints instead of a single object). This might be slower and a little bit more difficult to manage.
you have a copy of the pointer duplicated in every object. So: duplicated information which you should guarantee to keep valid.
your object is larger (two pointers instead of one)
You only have one positive note:
the access to the pointer is direct instead of having one indirection. This could mean that the access to the referred object is slightly faster with your implementation...
(Updated 14.4.2017)
I by myself tried out unique_ptr and shared_ptr and was bit surprised to see that those classes does not make your life easier. I had function in one API where function would pick up Object*& - fill it out (pointer) and after that you need to delete that object. It's possible to use c++11, but you need to add extra temporary pointer for that purpose. (So use of *_ptr classes does not makes my life easier)
How complex would it be to implement smart pointer ?
Just by quickly glancing auto_ptr class implementation, I've quickly coded simple smart point container class, but then I've noticed that I need to support multiple smart pointers referencing the same object pointer.
Ok, so how complex could it be to code reference counting - I through, and went to google - and found one interesting article about it:
http://www.codingwisdom.com/codingwisdom/2012/09/reference-counted-smart-pointers-are-for-retards.html
Somehow I tend to agree with author of that article and comments written in that article that reference counting makes life even more complex, but still trying to stick to plain C also sounds bit dumb.
I'll now add code snippet here of my own class, and if you want to get newest version, you can check in this svn repository: https://sourceforge.net/p/testcppreflect/code/HEAD/tree/SmartPtr.h
Below is older version.
#pragma once
//
// If you're using multithreading, please make sure that two threads are not accessing
// SmartPtr<> pointers which are cross linked.
//
template <class T>
class SmartPtr
{
public:
SmartPtr() : ptr( nullptr ), next( nullptr )
{
}
SmartPtr( T* pt ) : ptr( pt ), next( nullptr )
{
}
SmartPtr( SmartPtr<T>& sp ) : ptr( nullptr ), next( nullptr )
{
operator=(sp);
}
~SmartPtr()
{
release();
}
// Reference to pointer - assumed to be filled out by user.
T*& refptr()
{
release();
return ptr;
}
// Pointer itself, assumed to be used.
T* get()
{
return ptr;
}
T* operator->() const
{
return ptr;
}
T* operator=( T* _ptr )
{
release();
ptr = _ptr;
return ptr;
}
SmartPtr<T>& operator=( SmartPtr<T>& sp )
{
release();
ptr = sp.ptr;
if ( ptr ) // If we have valid pointer, share ownership.
{
if( sp.next == nullptr )
{
next = &sp;
sp.next = this;
} else {
SmartPtr<T>* it = &sp;
while( it->next != &sp )
it = it->next;
next = &sp;
it->next = this;
}
}
return *this;
}
void release()
{
if ( !ptr )
return;
// Shared ownership.
if( next != nullptr )
{
// Remove myself from shared pointer list.
SmartPtr<T>* it = next;
while( it->next != this )
it = it->next;
if( it == it->next->next )
it->next = nullptr;
else
it->next = next;
next = nullptr;
ptr = nullptr;
return;
}
// Single user.
delete ptr;
ptr = nullptr;
}
T* ptr; // pointer to object
SmartPtr<T>* next; // nullptr if pointer is not shared with anyone,
// otherwise cyclic linked list of all SmartPtr referencing that pointer.
};
I've replaced reference counting with simple linked list - linked list keeps all class instances referenced, each destructor removes one reference away.
I've decided to rename operator* to refptr() function just to avoid developers writing extra fancy code. ("C++ jewels")
So in general I agree with article above - please don't make smart pointers too smart. :-)
I'm free to any improvement suggestions concerning this class and potential bugfixes.
And I wanted also to answer to original author's questions:
Real question: Is this new Smart Pointer Class with simply a int *countPtr
sufficient enough? If yes, why bother to use an extra Use-Count Class
like in the book? If no, what do I miss?
You're using separate mechanics for count management, like article link above mentions - it will becomes non-trivial to follow and debug reference counting. In my code snippet I use linked list of smart pointer instances, which does not perform any allocation ( so implementation above is faster than any other existing smart pointer implementation ), also it easier debugging of smart pointer itself - you can check by link (next) who locks down your memory from being collected.
But in overall - if you are experiencing memory leaks, I would say that it's highly non-trivial to find where they are if you're not originally made that code. Smart class pointer does not help in that sense to figure out who and how much has leaked out. Better to code once and properly that to fight with your own beast later on.
For memory leaks I recommend to find existing tools and use them - for example this one:
https://sourceforge.net/projects/diagnostic/
(There are plenty of them, but none of them works reliably/good enough).
I know that you're eager to put dislike to this implementation, but really - please tell me what obstacles you see in this implementation ?!

Const reference to member or temporary

I have a class that holds data that I expect to use a lot, so I thought it would be best to return a const reference to it. However, given some parameters, I might need to create new data on the fly. I've seen that you can return a temporary to a constant reference like so:
class Foo {
public:
Foo() { ... } //Initialize data
LARGE_DATA getData(bool param1, bool param2) {
if (...) { // For some crazy function of the parameters
LARGE_DATA newData = ...; // Create new data and return it
return newData
}
return data; // Usually, will just use the default value
}
private:
LARGE_DATA data;
};
void bar() {
Foo f;
const LARGE_DATA& data = f.getData();
... // Process data (read-only)
}
but this seems like it would make a copy of data. I would like to do something like this:
class Foo {
public:
Foo() { ... } //Initialize data
const LARGE_DATA& getData(bool param1, bool param2) {
if (...) { // For some crazy function of the parameters
LARGE_DATA newData = ...; // Create new data and return it
return newData
}
return data; // Usually, will just use the default value
}
private:
LARGE_DATA data;
};
void bar() {
Foo f;
const LARGE_DATA& data = f.getData();
... // Process data (read-only)
}
so to avoid unnecessary copies. Visual Studio doesn't complain about this, but g++ does (and probably rightly so). What's the right way to do this?
I think I understand your dilemma. In your first implementation, you're relying on a feature of C++ described here where a temporary (newData in this case) returned from a function has its lifetime extended when a const reference to it is saved on stack by the calling function. But if data is returned instead of newData, it will create a copy and then return the copy as a temporary, which you don't want.
Your second implementation attempts to prevent making a copy of data by returning a reference, but this breaks with g++ because of newData. You are no longer returning a temporary object called newData, you are now returning a reference to it, which doesn't qualify for lifetime extension according to g++.
I see two ways out of this. You can either break getData() into two methods, one which will return data by returning a reference, and another which returns newData by value so a temp is created. Or you could save newData as a data member of the class, return a reference to that as you would to data, and let it be overwritten each time you need to recalculate it, which however only works if you only need to read a given value of newData between consecutive calls to getData().
You should probably either retain ownership by storing newData somewhere in Foo before returning a reference to it (maybe in a map that you lazily initialize where the key is a representation of the function arguments, like std::pair<bool, bool> in this case), or use some reference counted smart pointer (boost::shared_ptr/std::shared_ptr in C++11) to hold the newData if you don't want to retain ownership (also wrapping the data member in a smart pointer).
To me, it seems like a use-case for pointers.
Have the method getData return a pointer and if you don't want the returned object to be modified, have it return a const pointer. What the pointer points to - the member data or the temporary data you create on the fly - can be decided at runtime. This would also avoid copying of the data when the method returns.
In this approach, one thing you need to be cautious of is that the temporary data that you create would have to be on heap, otherwise the returned pointer will point to memory that is out of scope.

create an instance for a pointer in other scopes

I have two methods to create an instance for a pointer.
But one of them will fail.
class A {
public:
int num;
};
void testPointer1(A* a){
a = new A();
a->num = 10;
}
A* testPointer2(){
A* a = new A();
a->num = 10;
return a;
}
void testPointer() {
A* a1 = NULL;
testPointer1(a1); // this one fails
//cout << a1->num << endl; // segmentation fault
A* a2 = NULL;
a2 = testPointer2();
cout << a2->num << endl;
}
why is testPointer1 wrong?
The syntax is valid, but it doesn't do what you want because testPointer1() is operating on a copy of the pointer, not the actual pointer itself. So when you assign the address to the newly allocated object, it gets assigned to the copy, not to the original a1 pointer.
Because of this, the address is lost and you get a memory leak. Also, since the original a1 pointer was never modified in the first place, you attempted to dereference a null pointer, which is a bad thing.
I'd say testPointer2() is the better way to do it, but if you want testPointer1() to work, try this:
void testPointer1(A*& a)
{
a = new A();
a->num = 10;
}
The parameter type indicates "a reference to a pointer to A." That way, instead of a copy of the pointer being passed, a reference to the original pointer will be passed. A C++ reference is an alias to another object. So whatever you do on the alias, it gets performed on the original object.
Extra notes:
Note that the parentheses in new A(); are actually significant and their presence or absence makes a difference.
Also note that you must manually delete all new'ed objects after you're done with them, or you will get a leak. Typically you would wrap the pointer in its own class and implement RAII or use a smart pointer such as Boost's smart pointers or auto_ptr, for proper memory management and exception safety.
If you're going to set the value of num on initialization, why not create a constructor?
class A
{
public:
A(int n) : num(n) {}
int GetNum() const { return num; }
private:
int num;
};
void testPointer1(A*& a)
{
a = new A(10);
}
A* testPointer2()
{
return new A(10);
}
// auto_ptr example, see link in second note above
std::auto_ptr<A> testPointer3()
{
return auto_ptr<A>(new A(10));
}
The testPointer1 functions works on a copy of the provided pointer : modifications to a in testPointer1 are not reflected to the caller.
It's exactly like in this simpler example :
void testInt1(int i)
{
i++;
}
void testInt()
{
int i = 0;
testInt1(i);
// i is still 0
}
If you want the change in testInt1 to be reflected to the caller, you have to pass either a pointer or reference to i (and not just the value of i). The same solution can be applied to your specific case, though one could argue that pointers to pointer and references to pointer are not really a best practice.
Is this homework ?
This seems to be obvious:
formal parameters are saved on the stack & restored after method/function call.
then whatever f(type x), manipulating x inside the function/method won't change it's value outside of the function.
even if type is a pointer type.
the only way to make x change inside a function is to tell it is modifiable through references or pointer to type.
in your case :
A* a1 =NULL
call to your method won't change value of a1 outside of testPointer1
so a1 will still be NULL after the call.