template <typename Type>
bool Lazy_deletion_node<Type>::insert( Type const &obj ) {
if(this == nullptr){
Lazy_deletion_node<Type> *tmp = new Lazy_deletion_node( obj );
this = tmp;
return true;
}
else if(obj == this->retrieve()){
if(erased){
erased = false;
return true;
}
else{
return false;
}
}
else if(obj < this->retrieve()){
left()->insert( obj );
}
else if(obj > this->retrieve()){
right()->insert( obj );
}
}
Hey guys, I'm trying to to do an insert for a Lazy deletion tree, which is basically a binary search tree except that I mark the nodes as "erased" instead of actually removing them from the tree. This is a method to insert a new node into the tree containing the object obj. nullptr is defined to be 0.
I first test to see if the pointer is pointing to 0. If it is, I create a node that stores obj inside and then I try to make the pointer point to this newly created node.
However, when I try to compile, it gives me the error that lvalue is required as left operand of assignment on the line that reads this = tmp;. Can anybody tell me why this is happening?
Thanks in advance.
You can't reassign this. Like the error says, it's not a modifiable l-value.
However, if this was a pointing at an object that had a mutable field of the same type you're trying to assign, you could access it through this->myField or this.myField, depending on whether it's a pointer or a reference.
"this" is a reserved keyword that is a pointer to the object currently in scope (loosely similar to "self" if you're familiar with Python). It is designed to ALWAYS point to the object currently in scope, which means you cannot change its value.
What you're trying to do when you use "this = tmp;" is a little bit like trying to use "false = 10;"
Related
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");
thank you for checking my question, I currently have a really basic question on operator "delete", it seems it can automatically change the pointer value to nullptr. Let me give you a example for this:
template <typename T>
void Tree<T>::remove(const unsigned& index, TreeNode<T>*& tree)
{
if(tree == nullptr)
{
std::cerr << "remove: can't find target" << std::endl;
}
else if(index < tree->index)
{
remove(index, tree->left);
}
else if(index > tree->index)
{
remove(index, tree->right);
}
else if(index == tree->index)
{
if(tree->degree() == 2)
{
tree->index = findMin(tree->right)->index;
tree->value = findMin(tree->right)->value;
remove(tree->index, tree->right);
}
else
{
auto oldNode = tree;
tree = (tree->left != nullptr) ? tree->left: tree->right;
delete oldNode;
// oldNode = nullptr;
}
}
}
The code above is a classic searching tree remove algorithm. If the current tree only has two nodes which is root (with key equals to 3 for example) and right child (with key equals to 4 for example), so when I remove node 4, it will call remove twice and go to this line:
delete oldNode;
And this line will delete "oldNode", which should be 4 right now. As far as my knowledge, the delete operator will just free the memory address(the address is same to the value of oldNode), which means it tells OS this address is available again. So I suppose when I print out the value of root's right pointer(root->right), I should get a address. Actually when I print out I get 0. So my question when root->right changed?
Hope I explain my question clearly. That may be a stupid question, let me known if I make any confusion.
I think what you are seeing is that the use of a pointer after the delete is undefined behavior (until c++14).
For c++14: indirection through a pointer that became invalid in this manner and passing it to a deallocation function (double-delete) is undefined behavior. Any other use is implementation-defined.
Undefined behavior basically allows the implementation to do whatever it wants with the pointer after delete (even change its value).
It looks like your implementation sets the value of the pointer to nullptr in delete.
I found the answer, but before answer, let me clarify my question. The question is asking when a child is deleted, when and who set its parent right pointer to nullptr. The answer is this function has pointer reference as argument, when call this function, the passed parameter itself will be set to nullptr. For example, when pass in root->right, the root->right can be directly set to nullptr.
I am struggling to implement a method called find_set within my class blob. It is a recursive function which returns pointer to a blob object. My blob class is basically a linked list and this function is supposed to be passed a blob and then recursively traverse blob's parents until arriving at the head of the blob list.
I am at work so I am having to recreate the essentially components. This is not copy and pasted version of my code, but I want to know how to do it by the time I get home.
class blob{
public:
int size;
int index[2];
char value;
blob *parent;
blob *find_set(blob* &in_question);
};
The necessary elements of the blob class to understand my conundrum.
blob* blob::find_set(blob* &in_question){
if(in_question!=nullptr)
in_question.parent= find_set(&in_question.parent);
return in_question;
}
I hope I have been explicit enough.
So, I'm guessing the problem is here:
blob* blob::find_set(blob* &in_question){
if(in_question!=nullptr)
in_question.parent= find_set(&in_question.parent);
return in_question;
}
I'm not quite sure why you're taking a reference to a pointer, but here's a working version of the code:
blob* blob::find_set(blob* in_question){
if(in_question != nullptr)
in_question->parent = find_set(in_question->parent);
return in_question;
}
Accessing members of a pointer requires using -> instead of .
Taking the address of in_question->parent yields an object of type blob**, which is probably not what you want
If the purpose of the method is to get the "top blob", i.e. the blob that has no parent, I would change the name to get_top_blob or similar.
Naming aside, you need to change the recursion termination condition so that the last recursion call actually returns the "top blob" (currently, the last call returns nullptr).
blob* blob::get_top_blob(blob* in_question){
if(in_question == nullptr) //no blob -> no "top blob"
return nullptr;
else if(in_question->parent != nullptr) // get the "top blob" of the parent
return get_top_blob(in_question->parent);
else //in_question is the "top blob"
return in_question;
}
Update:
Or, in case you wish to "flatten" the tree at the same time (see the Enhancements section
in Disjoint set data structure (C). They're not using recursion and thus their code is easier to read IMO. And now that you have clarified the context, their naming is probably better than what I suggested)
blob* blob::get_top_blob(blob* in_question){
if(in_question == nullptr) //no blob -> no "top blob"
return nullptr;
else if(in_question->parent != nullptr) // get the "top blob" of the parent
{
top_blob = get_top_blob(in_question->parent);
in_question->parent = top_blob;
return top_blob;
}
else //in_question is the "top blob"
return in_question;
}
This is what the member function find_set(...) looked like after I got it working.
blob *blob::find_set(blob &in_question){
if(in_question.parent!=nullptr)
in_question.parent= find_set(*in_question.parent);
else
std::cout<<"("<<in_question.index[0]<<","<<in_question.index[1]<<")"<<std::endl;
return in_question.parent;
}
I am writing a method to Delete duplicate-value nodes from a sorted linked list in c++. I'm trying to use Node* instead of void return type but facing an error because of the return statement.
My method code..
Node* RemoveDuplicates(Node *head)
{
struct Node* current = head;
struct Node* next_next;
if(current == NULL)
return;
while(current->next != NULL)
{
if(current->data == current->next->data)
{
next_next = current->next->next;
free(current->next);
current->next = next_next;
}
else
{
current = current->next;
}
}
}
The compile time error message i am receiving..
solution.cc: In function 'Node* RemoveDuplicates(Node*)':
solution.cc:31:6: error: return-statement with no value, in function returning 'Node*' [-fpermissive]
return ;
^
Change the return type to void.
There is nothing valuable to be returned from the function.
The compiler doesn't pretend to know what you are thinking, he asks you to make contracts on what is going on. Hence, declaring the return type Node* you must provide an output of that specific type : a Node pointer. The most likely scenario I can imagine here would be returning the current node without the duplicates at the end of the function.
Node* RemoveDuplicates(Node *head)
{
// some instructions
return head;
}
so you can have this kind of semantic :
Node* distinctList = RemoveDuplicates(head);
if (distinctList) // NULL (0 / false) if empty
{
// some more instructions
}
However, if you don't need anything to go out of the function, the return type should be void (nothing).
Hope this helps!
I will treat this as a learning exercise, and ignore the fact that it is preferable to use a std list than to implement your own linked list, and it is preferable to use new and delete to using malloc and free.
If you specify Node* as a return type, you must return a pointer to a node. In order to answer your question, you have to ask is: what pointer do you want to return? As written you are deleting all duplicate pointers. Do you want to return the last pointer deleted? Do you want to loop until you find a duplicate and delete that?
You have two exit points in your code snippet. The first is a plain "return" statement, which is called when the list is empty. As written you are returning void, i.e. nothing. You need to return a pointer to a Node, but you have no valid pointers, so you probably want to return a null_ptr, which is a pointer to nothing.
Now we come to the part of your question which depends on the desired behavior. For example:
while(current->next != NULL)
{
if(current->data == current->next->data)
{
next_next = current->next->next;
free(current->next);
current->next = next_next;
/// Here you have a valid pointer you could return:
return current;
}
else
{
current = current->next;
}
// if you get here, no duplicates were found, so you can return a nullptr.
return std::nullptr;
}
Will loop over your list until a duplicate is found, will delete that duplicate, and return a pointer to the remaining pointer. If no duplicates are found, a nullptr is returned.
I leave it as an exersize to modify this to loop over all elements in the list until the last duplicate is found (hint, you will have to introduce a local variable to store the return value), and return that.
Good luck.
I have a function that returns a Customer object (not pointer) like this:
Customer CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
return false;
else{
Node *cur = find(index);
return (cur->data);
}
}
This function gets a Customer object from a CustomerList(which is a linkedlist).
I'm trying to manipulate the Customer in the list with following function(this function adds an Account to the Customer object.)
list.retrieve(i).addAccount(acc);
However after this function call, Customer object in CustomerList doesn't change. I assume that the reason is I return a copy of a Customer object, not the object itself.
So in order to return the adress of Customer and manipulate it correctly, i make the following changes to my function.
Customer* CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
return false;
else{
Node *cur = find(index);
return &(cur->data);
}
}
And call the manipulating function like that:
list.retrieve(i)->addAccount(acc);
But it gives me a "Access violation reading location 0x00000044." error. What I want to learn is:
Why doesn't it manipulate the Customer object in first place? Is my assumption right?
After I change my functions and function calls, why does it gives me the error I mentioned above?
Why doesn't it manipulate the Customer object in first place? Is my assumption right?
As you say, you're returning a copy and manipulating that, leaving the one in the list untouched.
After I change my functions and function calls, why does it gives me the error I mentioned above?
Almost certainly because of this:
return false;
That will return a null pointer if the index is out of bounds. If that's the behaviour you want, then you'll need to check before dereferencing the pointer:
if (Customer * c = list.retrieve(i)) {
c->addAccount(acc);
} else {
// handle the error?
}
and, out of politeness, you should return something that looks more like a null pointer such as nullptr, NULL, or 0.
It might be a better idea to throw an exception (perhaps std::range_error); then the caller can assume that the pointer is valid if the function returns. In that case, it might also be better to return a reference rather than a pointer, giving code very much like your original example:
Customer & CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
throw std::range_error("Customer index out of range");
else{
Node *cur = find(index);
return (cur->data);
}
}
list.retrieve(i).addAccount(acc); // Does exactly what you'd expect
I might also consider moving the range checks into the find function, if that seems appropriate.
Why doesn't it manipulate the Customer object in first place?
Yes you are right. by defualt its retuned by value not by reference so original object in List is not geting modified.
After I change my functions and function calls, why does it gives me the error I mentioned above?
I think you need to share the code of addAccount Method. the problem may be inside it.
Considering then with original code return by value it was working corectly (without exception).