What can I use as a NULL struct? - c++

I'd like to code a loop going through several instances of the same struct (named edg in my case) iterated by a certain function going from neighbor to neighbor until it returns an element that says STOP. I tried coding it using a NULL return but it doesn't work. What could I use?
Here is some code explaining it probably more accurately than my previous words:
My structure:
struct edg{
int i,j,k;
edg(int a, int b, int c){
i = a; j = b; k = c; //I'm adding a constructor to it
}
}
My iterating function:
edg neighbour(edg temp){
if(temp satisfies certain criterias){ return edg(new coordinates);}
else{ return NULL;}
}
My loop:
while(my_edg!=NULL){
my_edg = neighbour(my_edg);
}
I guess I could just pick a certain value of an edg define it as a rejection, and replace in my loop by:
while(my_edg!=edg_marked_as_rejection)
But is there another way to do so?

Note that your function:
edg neighbour(edg temp){
if(temp satisfies certain criterias){ return edg(new coordinates); }
else{ return NULL; }
}
returns an instance of edg by value thus trying to return NULL; is invalid (unless you've defined some custom conversion). NULL is a possible value when passing / returning by pointer, which in this case could mean changing the prototype of this function to:
edg* neighbour(edg temp) { ... }
however based on the semantics it would be more reasonable to either pass by reference and return a flag indicating success:
bool neighbour(const edg& temp, edg& result) {
if (...) {
result = ...;
return true;
}
return false;
}
or in case your condition "if(temp satisfies certain criterias)" should be met in most of cases and these criteria not being met is rather an exceptional state, you might also consider throwing an exception (instead of returning NULL).
A third option might be implementing a NULL object design pattern, meaning that instance of edg marked as "invalid" would be constructed and returned and caller would do something like:
edg n = neighbour(tmp);
if (!n.isValid()) {
...
}

Related

How to properly return null/empty object in C++?

I am a Java developer trying to solve a simple C++ task. In Java, if I want to return an empty/null object, to indicate that object is not found I just return null. Example:
Person getGetByName(String name) {
for (int i = 0; i < 10; i++) {
if (people[i].name == name) {
return people[i];
}
}
return null;
}
later in code I can do
Person p = getByName("Sam");
if (p == null) { ...
}
In C++ I have the following method
It seems that if I declare a method like the above, returning NULL is not an option since the method doesn't return a pointer but rather a class.
So what do I return to indicate that no person is found ?
If you're using C++17 or above, use std::optional.
Alternatively, return a value that would convey a similar meaning, e.g. a std::pair<bool, Person> Note that this would still require a Person object to be there, it merely does not need to be valid. If the objects are too large to be returned emptily like that, you'll have to find a workaround, like a union, or a pointer.
There's one more problem to the above code - the C++ version is inefficient. In C++, returning Person by-value always returns a copy of it (except for RVO, which is not applicable here).
Unlike C++, Java has no value semantics for objects, in Java returning Person always returns a reference to an object. A reference in Java works more like a pointer in C++, e.g. it can be null.
So the equivalent C++ code would actually be:
Person* getGetByName(std::string const& name) {
for(int i = 0 ;i<10 ;i++) {
if(people[i].name == name){
return &people[i];
}
}
return nullptr;
}
Now you can do
Person* p = getGetByName("blah");
if (p == nullptr) {
// not found ...
} else {
// use p->name etc.
}
C++ has no automatic memory management. The lifetime of the people vector must thus outlast the returned pointer. If that's not the case then it's more idiomatic to work with smart pointers instead (e.g. unique_ptr or shared_ptr). Thinking about object lifetimes is the responsibility of the C++ developer.
You could return the person object through a parameter and return a bool to indicate if something meaningful was returned through the parameter:
bool getGetByName(const std::string& name, Person& person)
{
for(int i = 0; i < 10 ;i++)
{
if(people[i].name == name)
{
person = person[i] // Writing in the person param (note the reference in the definition of the 'person' parameter).
return true; // Indicate success (i.e. non null).
}
}
return false; // Indicates null.
}
Later in code you could write:
Person aPerson;
if(getGetByName("A name", aPerson))
{
// aPerson is valid, do something with it.
}
// aPerson is not valid, handle it.
In Java, class instance variables and such returned objects are actually references/pointers. Since they are pointers, they can point to null. Primitive types such as int on the other hand are not pointers. Since they aren't pointers, they cannot point to null. You cannot have int x = null;.
In C++, class instance variables and such returned objects are not pointers. Like the Java primitives, they cannot point to null.
How to properly return null/empty object in C++?
If the type of the object has a value that represents null or empty, then simply return that value. Here is an example where that type is a pointer:
int* function()
{
return nullptr;
}
When a type has an empty value, such value can usually (but not necessarily) be created using value initialisation. Here is an example that returns an empty vector:
std::vector<int> function()
{
return {};
}
If the type doesn't have a representation for null or empty, then you cannot return a null or empty value that doesn't exist.
However, using type erasure techniques, it is possible to design a class that internally contains either a value of another type, or doesn't. Using a template, such class can be used to augment any type with an empty value. The standard library comes with such template: std::optional.
As for your particular example, idiomatic solution in C++ is to return an iterator to the found element, and return an iterator to the (one past the) end of the range if nothing is found. Iterator is a generalisation of a pointer. Your example re-written in C++:
auto getGetByName(std::string_view name) {
auto first = std::begin(people);
auto last = std::end(people);
for (; first != last; ++first) {
if (*first == name) {
return first;
}
}
return last;
}
// later
auto it = getByName("Sam");
if (it == std::end(people)) { ...
}
Note that there is no need to write that function, since the C++ standard library provides implementation of linear search. Simply call:
auto it = std::ranges::find(people, "Sam");

C++ Statement can be simplified

Apologies for the lame question. I am using Intellij Clion Student licensed version for my C++ curriculum. As a part of implementing an UnsortedList class, we had to write a method isInTheList to see if an element is present in the array. The class implementation goes as
bool UnsortedList::isInTheList(float item) {
for (int i = 0; i < length; i++) {
if (data[i] == item) {
return true;
}
return false;
}
}
However, the ide shows a coloured mark at data[i] == item with a popup saying
Statement can be simplified less... (Ctrl+F1)
This inspection finds the part of the code that can be simplified, e.g. constant conditions, identical if branches, pointless boolean expressions, etc.
For a previous method to check if the list if empty, I used the following simplified form instead of if-else statement.
bool UnsortedList::isEmpty() {
return (length == 0);
}
However, with iteration involved now, I cannot come up with a simplified statement in the former. Any help is much appreciated. Thank you.
Fix
Your return false should be moved outside off the for loop.
Because you accidentally put it inside the for loop, this iteration never execute for the second time.
So your IDE thinks the for loop is pointless, and suggests you to simplify it to:
return data[0] == item;
This obviously isn't what you want. So really this is just a one-line shift to make it right.
Why not use STL?
inline bool UnsortedList::isInTheList(float item) {
return std::find(data, data+length, item) != data+length;
}
std::find returns an iterator pointing to the element if it's found, or an iterator equal to one-past-last item (i.e. exactly the second argument passed) if nothing is found. You can use a simple equality check to decide if one is found.
You are actually returning after one iteration in your loop. That's the remark of your compiler.
Your code could be simplified by easy write this:
bool UnsortedList::isInTheList(float item) {
if (length != 0) {
return data[0] == item;
}
}
Note, that this is still undefined behavior (UB). You do not have a return in all of your execution paths.
If your list is empty, you never enter the loop, which results in an UB, because there is no return statement, but the function has to return a bool.
I think, your intention was, to write something like this.
bool UnsortedList::isInTheList(float item) {
for (int i = 0; i < length; i++) {
if (data[i] == item) {
return true;
}
}
return false;
}
Move the return false; out of your for loop and you will be fine (still there are better ways to implement this, but that's another topic).

Returning pointer to global array from function

I am not experienced enough in C/C++ programming, so I am asking for an explanation.
I have global array declared as following. ASAK it is located in seperate memory part of initialized global memory in context of process memory.
Sensor sensorsArray[SENSORS_COUNT] = {dhtTempSensor, dhtHumSensor, dallasTempSensor, waterLevelSensor};
I need to find element in this array and return pointer to it (because I am going to change its value). I have written such function.
Sensor* getSensorById(uint32_t id) {
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
return current;
}
}
}
Will it work properly, I am not sure about current pointer, it is allocated on the stack so it is in function scope, will it be poped from the stack after function ends ? Or it will work properly.
I mean not pointer(address of array element which is taken using &sensorsArray[i]), but current pointer variable which contains address of erray element, will it be poped or not.
Please suggest best way how to do in such situation.
Thx.
You aren't covering all the possible returning cases of the function, namely, the case when the id does not match with any of the ids of the array.
Currently the pointer will return the last element of the array if there is no match.
You could correct that by defining the pointer Sensor* sensor_found = nullptr outside the for loop such that if there is no sensor found the return value is still valid, i.e. nullptr and assigning the found value of current to sensor_found, only if there is a match.
Sensor* getSensorById(uint32_t id) {
Sensor* sensor_found = nullptr;
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
sensor_found = current;
break;
}
}
return sensor_found;
}
If the id found return current, otherwise, if there is no match return nullptr.
you want to make sure that the function has a valid return statement on its every execution path. In you current implementation if the id is not matched then the return value of Sensor* is not set and will contain random bytes. One wau to deal with this situation is to return the nullptr to indicate that the Sensor was not found. Other than that, ythe function will work properly.
Sensor* getSensorById(uint32_t id) {
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
return current;
}
}
return nullptr; // id not matched
}
Your code is fine (as the comments suggest). The reason why you don't need to worry about the current pointer becoming invalid is because the memory that it points to (ie, the global array) stays valid beyond the scope of the function. Just because you happen to create a pointer (and remember, a pointer is really just a number that corresponds to some place in memory) to that memory doesn't mean that it becomes invalid when used elsewhere.
When you say Sensor *current = &sensorArray[i];, then if sensorArray[i] is stored at, say, position 0x10 in memory, then current = 0x10, and no matter where it is used, then sensorArray[i] will still be at memory location 0x10. When you assign a value to current, you are not copying the value from the sensor, you are merely getting a pointer to it.

How to find an element in a binary search tree?

I've been trying to build a 2-3 node. The adding function is working properly and has been confirmed by me so far. The only problem is the find function, which is called to find an element inside the 2-3 node. It does not seem to be working at all. The match pointer inside the it does not take the returned valued from the find_rec method at all, even though I already assigned it. It's just getting a new given address whenever the function is called and I have no idea why it does that. Can anyone help me out ? and tell me what I did wrong ? Thank you
**LValue and RValue**
E LValue() {return _first._value;}
E RValue() {return _second._value;}
**find function**
// Assuming this set contains an element y such that (x == y),
// return a reference to y. Such a y must exist; if it does not an
// assertion will fail.
E& find(E& x)
{
// the match pointer is supposed to take
// returned pointer from the find_rec function
// Yet, it is not doing that at all.
E* match = find_rec(x, _root);
assert(match != nullptr);
return *match;
}
**find_rec function**
// Helper function: find recursion
// function returns a pointer
E* find_rec(E& x, BNode<E>* root)
{
if(root == nullptr)
return nullptr;
else
{
// 2-node
if(!root->IsThree())
{
if(x == root->LValue())
return &root->LValue();
else if (x < root->LValue())
return find_rec(x, root->GetLeft());
else
return find_rec(x, root->GetRight());
}
// 3-node
else
{
if(x == root->LValue())
return &root->LValue();
else if(x == root->RValue())
return &root->RValue();
else if(x < root->LValue())
return find_rec(x, root->GetLeft());
else if(x < root->RValue())
return find_rec(x, root->GetMiddle());
else
return find_rec(x, root->GetRight());
}
}
}
The code is clearly able to return a nullptr when the desired value is not present in the tree.
The moment it gets into that situation, the assert will trigger and the *match return would fail. I expect you need to change the function signature to provide a return type that allows for this case.
From the code seems you're returning the address of a local temporary.
I cannot be sure because the LValue() method declaration is not visible, but if it's returning the node content by value and not by reference then the find_rec function will just return garbage (the address of a temporary allocated on the stack).
A decent compiler should issue a warning for this, by the way.

How can I refer to what called a function in C++?

I was wondering if there is a way in C++ to know what called a function? Like the this keyword in Java or JavaScript.
For example, I have a function called insert, which inserts an item into a linked list, I want the linked-list that called those the function insert to call two other functions. How would I do that?
I have this right now, is this valid?
bool linked_list::insert( int i )
{
bool inserted = false;
if( this.is_present(i) ) /* function is_present is defined earlier checks if an int is already in the linked-list. */
{
inserted = true // already inside the linked-list
}
else
{
this.Put( 0, i ); /* function Put is defined earlier and puts an int in a linked-list at a given position (first param.). */
inserted = true; // it was put it.
}
return inserted;
}
For historical reasons, this is a pointer. Use -> instead of ..
bool linked_list::insert(int i) {
bool inserted = false;
if(this->is_present(i)) {
inserted = true; // fixed syntax error while I was at it.
} else {
this->put(0, i); // fixed inconsistent naming while I was at it.
inserted = true;
}
return inserted;
}
Usually it is not needed to use this-> at all; you can just do if(is_present(i)).
this works in c++ the same as it does in Java. The only difference is that you need to use this-> instead of this. this is a pointer than therefor you cannot use the dot operator to access it's members.
why don't you just call the other functions in linked_list::insert(int)? And no, it is not valid, you should put this -> something instead of this.something