Is it possible to export a local created pointer?

In my CPP Basex client (=XML-database) I want to iterate over a set of results. I have copied the logic I found in a Java-client to the following C++ code. (I have also used this logic in my R-client so I know that the logic is correct:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ByteVect QueryObject::Next() {
	if (More()) return Cache -> at(Pos++);
	else {
		return ByteVect {};
	};
};
bool     QueryObject::More() {
	if (!Cache) {
		Results();
		Pos = { 0};
		// debug_dump(	Cache-> at(Pos));
	};
        if (Pos < Cache ->size()) return true;
        Cache = nullptr;
        return false;
};
void    QueryObject::Results() {
	ByteVect exec, result;
	pushByte(0x04, exec).addVoid(QueryID, exec);
	handShake(exec, result);
	Response.setValues((std::string) __FUNCTION__, result);
	VectOfByteVect interim = ResponseObj::splitter(Response.Splitted.at(0));
	Cache = &interim;
	debug_dump(Cache-> at(0));
};

I use these typedefs:
1
2
typedef std::vector<std::vector<std::byte>> VectOfByteVect;
typedef std::vector<std::byte>              ByteVect;

QueryObj.h contains the following declarations:
1
2
3
4
5
ByteVect    Next();
bool        More();
void        Results();
VectOfByteVect* Cache { nullptr };
int Pos;

A call to QueryObject::Next() results in a call to QueryObject::More().
After checking if 'Cache' != nullptr, QueryObject::Results() is called.

If I call QueryObject::Results() I receive the expected output. Line 24 shows that Cache points to an VectOfByteVect.
But as soon as I leave QueryObject::Results() and return to line 10 in QueryObject::More() Cache has become a dangling pointer. Uncommenting line 11 results in a crash.

I experimented with the 'new' keyword to explicitly allocate memory but I can't compile that code.

How can I create a working 'Cache' pointer?
Yes, L23 is a non-no as interim is local to the function and is only 'valid' for L22, 23, 24.

Can interim (possibly with a different name) be defined as a class member? It's then valid for all class member functions.
Since Cache is already a class member, what is the difference between making 'interim' a class member and changing line 22 into
VectOfByteVect Cache = ResponseObj::splitter(Response.Splitted.at(0));

?
Last edited on
You cannot use the pointer of a local variable outside the encompassing function. The local variable can only be copied/moved. Make sure that the buffer copied to (the Cache) is large enough to hold the data.

Why do you make the Cache a pointer at all?

What you can do is this:
1
2
3
4
	if(Cache == nullptr)
	  Cache = new VectOfByteVect;
	(*Cache) = ResponseObj::splitter(Response.Splitted.at(0));
	Cache = &interim;
Last edited on
@coder777

Why do you make the Cache a pointer at all?

I just copied the original logic (in Java).

Following this logic L8 Cache == nullptr means that Cache is empty and this results in calling L9 QueryObj::Results().

I'll try your suggestion.

EDIT
The incorporation of the proposed enhancement does indeed appear to work, but did reveal a bug. Fixing it should not be too difficult but should be done carefully.
Thanks
Last edited on
In Java, there is no distinction between variables/members containing a pointer/reference to an object and variables/members containing the object directly; the latter doesn't really exist in Java (except for primitive types). Instead, in Java, you are always dealing with references to objects; the objects always live in the "heap" space and your variables contain references to those objects. End of story.

In C++ this is quite different! You can allocated objects on the "heap" space, by using the new operator, which will give you a pointer to the object; such objects must explicitly be destroyed, using the delete operator, or else you will get a memory leak. Keep in mind that there is no garbage collection! But, just as well, in C++ you can have objects that "live" directly in the variable/member. This is called "automatic storage duration" and does not require or allow explicit destruction; such objects are destroy as soon as they go out of scope.

In your Results() function, the local VectOfByteVect-object interim has "automatic storage duration"; it lives on the stack (not heap) and it will be destroyed as soon as the surrounding function returns. Storing a pointer to interim in the member variable Cache therefore means that you are storing a pointer that becomes invalid ("dangling") at the very moment when the Results() function returns!

You probably want to change the type of member variable Cache from VectOfByteVect* to VectOfByteVect, so that it can store the actual object, rather than just a pointer to an object. However, be aware that, when assigning a member variable of type VectOfByteVect from another VectOfByteVect object, then this will invoke the assignment operator, i.e. it is an actual "copy" operation.

https://cplusplus.com/articles/y8hv0pDG/
Last edited on
I've adapted the functions as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ByteVect       QueryObject::Next() {
	if (More()) {
		ByteVect Next = Cache -> at( Pos++);
		return Next;
	}	else return ByteVect {};
};
bool           QueryObject::More() {
	if (!Cache) {
		Cache = new VectOfByteVect;
		(*Cache) = QueryObject::Results();
		Pos = { 0 };
	};
  if (Pos < Cache ->size()) {
  	return true;
  } else {
  	delete Cache;
        Cache = nullptr;
        Pos = { 0 };
        return false;
  };
};
VectOfByteVect QueryObject::Results() {
	ByteVect exec, result;
	pushByte(0x04, exec).addVoid(QueryID, exec);
	handShake(exec, result);
	result.pop_back();
	Response.setValues((std::string) __FUNCTION__, result);
        return Response.Splitted;
};

At first valgrind complained about the new VectOfByteVect in L9. But after adding the delete statement in L16 this was solved.
After solving the first problem, valgrind still reported 1 leak which was probably also caused by a missing delete statement.
When creating a connection to the database, I use this schema:
BasexClient class inherits from Base class.
Base class instantiates a BasexSocket.
1
2
3
4
5
6
7
BasexClient::BasexClient(const std::string& DBHOST, const std::string & DBPORT,
                         const std::string & DBUSERNAME, const std::string & DBPASSWORD)
           : Base{DBHOST, DBPORT, DBUSERNAME, DBPASSWORD}{};

Base::Base(const std::string& DBHOST, const std::string & DBPORT,
           const std::string & DBUSERNAME, const std::string & DBPASSWORD)
           : Socket(new BasexSocket(DBHOST, DBPORT, DBUSERNAME, DBPASSWORD)){};

This the trace from valgrind:
1
2
3
4
5
4 bytes in 1 blocks are definitely lost in loss record 1 of 8
operator new(unsigned long) (vg_replace_malloc.c:472)
Base::Base(...) (Base.cpp:12) =L7
BasexClient::BasexClient(...) (BasexClient.cpp:16) =L3
main (libBasexTest.cpp:22)


After some tries I found that after adding delete Socket to the BasexClient destructor, valgrind didn't report errors any longer.

@kigar64551
AFAIK memory is automatically released when an application stops without errors. Does this mean that the attention paid to removing objects is actually superfluous?
@kigar64551
AFAIK memory is automatically released when an application stops without errors. Does this mean that the attention paid to removing objects is actually superfluous?

Absolutely not !!!

Yes, when a process terminates, all of its memory is released by the operating system. But this does not mean that you don't have to destroy your objects while the program is running! It is a good practice that you destroy all of your objects before your program terminates.

(Leaving a few objects around when the program terminates is "okayish", but still bad practice)

The real problem is: If you never destroy your objects, then they will quickly pile up over the runtime of your program. This is called a "memory leak", and it will waste a lot of valuable memory! Also, for a long-running program, or a program that creates a huge number of objects, a "memory leak" can easily use up all of the computer's memory and make your program crash with "out of memory" error...

(In fact, the operating system will just kill processes that hog too much memory)

To make a long story short: Always destroy objects, as soon as they are not needed anymore, or you will be in trouble! (sooner or later)


You may want to learn about smart pointers, as they can make things a lot easier for you:
https://www.geeksforgeeks.org/smart-pointers-cpp/
Last edited on
You should delete the acquire memory in the correct order. Otherwise you risk a a crash or undefined behavior at the end of the program.

The best would be using smart pointer (i.e. unique_ptr or shared_ptr).
Topic archived. No new replies allowed.