I am writing a simple code, in which I have a list of objects of class Person.
The Person class
class Person
{
private:
std::string name;
std::string surname;
int year;
public:
Person(const std::string& personName,
const std::string& personSurname,
const int& personYear ):
name(personName), surname(personSurname), year(personYear) {}
Person(const Person& p): name(p.name), surname(p.surname), year(p.year) { }
Person(Person &&p) noexcept: name(std::move(p.name)), surname(std::move(p.surname)), year(std::move(p.year)) { }
int getYear()
{
return year;
}
void print()
{
std::cout << name << " " << surname << " " << year << std::endl;
}
};
in which, the move constructor is
Person(Person &&p) noexcept: name(std::move(p.name)), surname(std::move(p.surname)), year(std::move(p.year)) { }
I also have the node structure
struct node
{
Person data;
node *next;
inline node(const std::string& personName,
const std::string& personSurname,
const int& personYear): data(personName, personSurname, personYear), next(nullptr) { }
inline node(const Person& personToInsert): data(personToInsert), next(nullptr) {}
inline node(Person &&personToInsert): data(std::move(personToInsert)), next(nullptr) {}
};
whose move constructor is
inline node(Person &&personToInsert): data(std::move(personToInsert)), next(nullptr) {}
and finally I have the list class
class list
{
private:
node *head;
public:
list();
~list();
void insert(const std::string&, const std::string&, const int& );
void insert(const Person& );
void insert(Person &&);
void print();
};
whose move constructor is
void list::insert(Person &&personToInsert) {
node *new_node = new node(std::move(personToInsert));
if(head == nullptr || (head->data).getYear() >= (new_node->data).getYear())
{
new_node->next = head;
head = new_node;
}
else
{
node *current = head;
while(current->next != nullptr && (current->next->data).getYear() < (new_node->data).getYear())
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
My question is: inside the move constructors, is the use of std::move correct? Particularly in the first line of code of list::insert
inside the move constructors, is the use of std::move correct?
Yes.
Particularly in the first line of code of list::insert
This is correct, too.
Note, however, that there are two minor things I would like to point out. First, there is no need to manually define the move constructor for Person. If your class doesn't do manual resource handling (memory, IO handles etc.), just rely on the special member functions that the compiler generates for you. In this case, I would just remove Person's move and copy constructor.
Second,
inline node(Person &&personToInsert)
is not a move constructor. It's an ordinary constructor that happens to take an rvalue-reference parameter. Move constructors are for constructing an object from an rvalue reference of their own type.
Third,
void list::insert(Person &&personToInsert)
is not a constructor at all - it's an ordinary member function that happens to accept an rvalue parameter.
Related
A class (dLinkedList) need to have different c-tors: [please don't suggest to use STL containers!]
explicit dLinkedList(const int value);
dLinkedList(const dLinkedList &rhs);
explicit dLinkedList(size_t numberOfNode, int initializationValue);
dLinkedList(const std::initializer_list<int> &arg);
However, the c-tor with initializer_list seems to be not working. For example, if we construct a class like this:
dLinkedList test1 {10, 108}; // intention is to create 10 elements of 108 value each
But my question is -- based on the arguments - this c-tor can well be considered as one with initializer_list<int>. How do I resolve this conflict?
The implementation of the last two constructors
and the structure of the class dLinkedList are as follow:
class dLinkedList
{
public:
//constructors will go here
explicit dLinkedList(const int value)
{
createFirstNode(value);
nodeCount = new size_t;
updateNodeCount();
}
dLinkedList(const dLinkedList &rhs)
{
Node *temp = rhs.head;
createFirstNode(temp->data);
temp = temp->next;
while(temp)
{
push_back(temp->data);
temp = temp->next;
}
nodeCount = new size_t;
updateNodeCount();
}
explicit dLinkedList(size_t numberOfNode, int initializationValue)
{
createFirstNode(initializationValue);
for(size_t i = 1; i < numberOfNode; ++i)
push_back(initializationValue);
nodeCount = new size_t;
updateNodeCount();
}
/* constructor with std::initializer_list is not working!!
dLinkedList(const std::initializer_list<int> &arg)
{
std::cout << "\n Here\t";
if(arg.size() == 0) dLinkedList(1);
else
{
for(auto it = arg.begin() + 1; it != arg.end(); ++it)
{
push_back(*it);
}
nodeCount = new size_t;
updateNodeCount();
}
}*/
//class destructor will go here
~dLinkedList()
{
clear();
delete nodeCount;
nodeCount = nullptr;
}
//member functions will go here
void push_back(int); // will attach a new node at the end of the list
void push_front(int); // will insert a new node at the beginning of the list
bool insertNode(int, int, bool, bool); // will insert a new node after the existing node (true = first occurrence from the head with value int OTHERWISE if false, then from the tail.)
bool deleteNode(int, bool); // will delete the existing node (true = first occurrence from the head with value int OTHERWISE if false, then from the tail.)
void pop_back(); // will delete the last node in the list
void pop_front(); // will delete the first node in the list
size_t size(); // will return the number of nodes/elements - experimental feature
void printList(bool); // will print the values of the data - (true for ordered list, false for reverse ordered list)
void swap(dLinkedList &rhs); // will swap this linked-list with rhs
//operator overloading go here
dLinkedList& operator = (const dLinkedList &rhs);
dLinkedList& operator + (const dLinkedList &rhs);
dLinkedList& operator += (const dLinkedList &rhs);
dLinkedList& operator >> (const size_t numberOfNodes);
dLinkedList& operator << (const dLinkedList &rhs);
private:
//defining the double linked-list structure
struct Node
{
int data; //this is a generic place holder - will be replaced later with some actual data-structures
Node *next;
Node *previous;
explicit Node(int x) : data(x), next(nullptr), previous(nullptr) {}
};
//member functions go here
void createFirstNode(int val); //will create the first node when the list is empty
void clear(); // will be called when class destructor is called
void updateNodeCount(); // keeps the nodeCount variable up-to-date
bool empty(); // returns true if the list is empty
//some experimental utility functions for internal use
void ectomizeAndClip(Node*);
Node *detectData(int, bool);
void insertBefore(Node*, int);
void insertAfter(Node*, int);
//member variables go here
Node *head {nullptr};
Node *tail {nullptr};
size_t *nodeCount {nullptr}; //experimental feature
};
You do not need to do anything to "resolve the conflict". The language does it for you; initialization in the form
dLinkedList test1 {10, 108};
will always call an initializer_list constructor if possible, falling back to a non-initializer_list constructor only if there are no viable initializer_list constructor candidates.
The problem is that the resolution of the potential conflict did not produce the outcome that you wanted. You want it to be resolved in favour of the two-element constructor.
It is possible to simulate what you want by having the initializer_list constructor delegate to another function only if it has two elements:
explicit dLinkedList(size_t numberOfNode, int initializationValue) {
initialize(numberOfNode, initializationValue);
}
dLinkedList(const std::initializer_list<int> &arg) {
if (arg.size() == 2) {
initialize(arg[0], arg[1]);
return;
}
// otherwise: initialize the list using the values in `arg`
}
private:
void initialize(size_t numberOfNode, int initializationValue) {
// create `numberOfNode` nodes with value `initializationValue`
}
However, I would strongly recommend that you not do this, because it will be terribly confusing for the user if the meanings of the following three declarations are not similar:
dLinkedList test1 {1};
dLinkedList test2 {2, 3};
dLinkedList test3 {4, 5, 6};
In fact, a constructor that takes the number of nodes as the first argument, and the value with which to initialize those nodes as the second argument, is a bad idea in general. It will lead to bugs when users forget which argument is which.
A better way is to create a struct that has the desired number of nodes and desired initial value as members:
struct dLinkedListInit {
int numberOfNodes;
int initialValue;
};
explicit dLinkedList(dLinkedListInit i) {
// ...
}
With C++20 designated initializers, this constructor could be used as follows:
dLinkedList l({.numberOfNodes = 10, .initialValue = 108});
Also, strike the constructor explicit dLinkedList(const int value); since the initializer list constructor can provide the same functionality. It is good to make users write out the braces explicitly to make it obvious that the value inside the braces is meant to be interpreted as an element value.
I am trying to understand move semantics and in particular how std::move() works.
I understood that it's basically a static-cast to an rvalue reference type, but this exercise gets me confused. I have implemented a Node class following the Rule of Five (I know I could have followed the copy and swap idiom for a better implementation).
class Node
{
public:
Node(char data = ' ', Node *left_child = NULL, Node *right_child = NULL) : _data(data), _left_child(left_child), _right_child(right_child)
{
std::cout << "NODE CONSTRUCTOR" << std::endl;
}
Node(const Node& node);
Node(Node&& node) noexcept;
GetData() const;
Node& operator=(const Node &n);
Node& operator=(Node&& node) noexcept;
~Node();
protected:
Node *_left_child;
Node *_right_child;
char _data;
};
char Node::GetData() const
{
return _data;
}
Node::Node(const Node& node)
{
...
}
Node::Node(Node&& node) noexcept
{
std::cout << "MOVE CONSTRUCTOR" << std::endl;
this->_data = node.GetData();
this->_left_child = node._left_child;
this->_right_child = node._right_child;
node._right_child = nullptr;
node._left_child = nullptr;
}
Node& Node::operator=(Node&& node) noexcept
{
std::cout << "MOVE ASSIGNMENT OPERATOR " << std::endl;
if(&node != this)
{
if(this->_right_child != nullptr)
{
delete this->_right_child;
}
if(this->_left_child != nullptr)
{
delete this->_left_child ;
}
}
this->_data = node.GetData();
this->_left_child = node._left_child;
this->_right_child = node._right_child;
node._right_child = nullptr;
node._left_child = nullptr;
return *this;
}
Node::~Node()
{
delete _left_child;
delete _right_child;
}
Node& Node::operator=(const Node &n)
{
...
}
Then in my main() function:
int main() {
Node *NodeOne = new Node{};
Node NodeTwo{};
std::stack<Node*> stack_of_nodes_ptr;
stack_of_nodes_ptr.push(std::move(NodeOne));
delete stack_of_nodes_ptr.top();
stack_of_nodes_ptr.pop();
std::stack<Node> stack_of_nodes;
stack_of_nodes.push(std::move(NodeTwo));
return 0;
}
The output is:
NODE CONSTRUCTOR
NODE CONSTRUCTOR
CALLING NODE BASE DESTRUCTOR
MOVE CONSTRUCTOR
CALLING NODE BASE DESTRUCTOR
CALLING NODE BASE DESTRUCTOR
My doubt arises seeing that the move constructor is not called in the first push_back() but only in the second one. The only difference here is that the first stack is of Node* pointers while the other one is a stack of Node objects.
Could you please tell me why, in case of raw pointer, move constructor is not called?
Could you please tell me why in case of raw pointer std::move is not called?
std::move is called. But std::move doesn't print anything. Remember that std::move is:
basically a static cast to an rvalue reference type
Calling std::move never causes any output.
My doubt arises seeing that the move constructor is not called in the first push_back
You didn't move an object of type Node, so the move constructor of Node was not called.
For all fundamental types, moving is same as copying. Pointers are fundamental types.
I am attempting to create a doubly linked list and use a function which accepts a value passed by reference. However, when I try to access the value it throws an error. I am getting the "error: lvalue required as left operand of assignment &da= NULL;"
I have tried:
#ifndef __DOUBLYLINKEDLIST_H__
#define __DOUBLYLINKEDLIST_H__
//
//
#include
#include
using namespace std;
class DoublyLinkedList {
public:
DoublyLinkedList();
~DoublyLinkedList();
void append (const string& s);
void insertBefore (const string& s);
void insertAfter (const string& s);
void remove (const string& s);
bool empty();
void begin();
void end();
bool next();
bool prev();
bool find(const string& s);
const std::string& getData() const;
private:
class Node
{
public:
Node();
Node(const string& data);
~Node();
Node* next;
Node* prev;
string* data;
};
Node* head;
Node* tail;
Node* current;
};
DoublyLinkedList::Node::Node(const string& da)
{
this->data=nullptr;
this->next=nullptr;
this->prev=nullptr;
&da= NULL;
}
The line
&da= NULL;
is trying to set NULL to the address of the variable da. You can't do that.
You might mean
this->data = &da;
which would work (as in, compile), but would probably cause errors if the string passed as data goes out of scope before your list does (which is very likely).
What you probably actually want, if you're going to use a string*, is
this->data = new string(da);
which dynamically allocates a new string, giving it da to copy from. In the destructor, you'd then want something like
if (data != nullptr) delete data;
I'm not a Standards guy, so can't give you the technical explanation of lvalues and such.
I am trying to fix a problem related to overloading assigment operator to be able to assign one list to another.
I have two classes:
class CarList{
private:
Car* head;
void clear();
public:
CarList();
~CarList();
CarList(const CarList & olist);
CarList & operator= (const CarList & olist);
void add_car(char* name, int number, int id);
void remove_make(char* name);
};
and
class Car{
private:
char* make;
int license_number;
int owner_ID;
Car* next;
public:
//constructor with data presentation
Car(char* name, int number, int id);
Car(const Car &car);
//destructor with data presentation - for further use
~Car();
//Rest of methods
Car*& GetNext();
char* GetMake();
int GetLic();
int GetID();
int SetID(int id);
void Print();
};
I wrote following method to overload an operator:
CarList& CarList::operator= (const CarList & olist)
{
clear();
if (this == &olist) return *this; // detecting if target is same as source
if(this->head != NULL) this->head=new Car(*olist.head);
return *this;
}
which calls Car copy constructor:
Car::Car(const Car &car)
{
this->make = new char[strlen(car.make)+1];
strcpy(this->make, car.make);
this->license_number = car.license_number;
this->owner_ID = car.owner_ID;
if (car.next != NULL)
this->next = new Car(*car.next);
else
this->next = NULL;
}
but program crashes and I don't really know why. Copy constructor in Carlist works totally okay using Car copy constructor call.
How do you call a copy constructor for template class that has private member which is also another template object
I am building a stack class which uses the list class to build up a stack.
The list class has a copy constructor,
so I want to copy stack1 = stack2
How do I call it in the copy constructor of the stack copy constructor
The last code is the copy constructor for the stack class and I am trying to copy the private member list_3 myData
When I do myData = src.myData; // It copies the same address and doesn't give a new object
template <class ItemType>
class List_3
{
public:
typedef size_t size_type;
List_3();
List_3(const List_3 & src);
~List_3();
void insert(const ItemType & item);
void remove();
void reset();
bool advance();
bool isEmpty() const;
bool atEOL() const;
bool isFull() const;
ItemType getCurrent() const;
private:
struct Node {
ItemType value;
Node* next;
Node* previous;
};
Node* head;
Node* tail;
Node* cursor;
};
//Copy Constructor for List class**
template<class ItemType>
List_3<ItemType>::List_3(const List_3<ItemType> & src)
{
//Copy constructor
head = NULL;
tail = NULL;
cursor = NULL;
Node *tempCursor = new Node;
tempCursor = src.head; // copying the original list from head to tail
if (!src.isEmpty()) { //if the src list is not empty start copy process
while (tempCursor != NULL)
{
insert(tempCursor->value);
cursor = NULL;
tempCursor = tempCursor->next; //Move to the next item in the list use previous if copying from tail
}
reset(); //Reset the cursor
}
}
**//============================================================================**
template<class ItemType>
class Stack_3 {
public:
typedef int size_type;
Stack_3();
//Copy constructor
Stack_3(const Stack_3 & src);
void makeEmpty();
bool isEmpty() const;
bool isFull() const;
void push(const ItemType &);
ItemType pop();
private:
List_3<ItemType> myData;
};
**//Copy Constructor for Stack Class**
template<class ItemType>
Stack_3358<ItemType>::Stack_3358(const Stack_3358<ItemType> & src)
{
myData = src.myData;
}
You need a initializer list to achieve this
Stack_3358<ItemType>::Stack_3358(const Stack_3358<ItemType> & src)
: myData(src.myData) // this calls List_3 copy constructor
{
}
myData = src.myData; will use the copy assigned operator which will use same address unless copy assignment operator is overloaded.
How do you call a copy constructor?
You don't call constructor. A constructor runs automatically when you create an object of the corresponding class