2

So I have a class, lets call it Cow. This cow has an age and some other variables like a static number of Cows alive variable.

Well I want to make a linked list, and I made the class for it and the node structure inside of the linkedlist class, looks a bit like this.

struct node{
        Cow data;
        node* next;
};

Then there's my addNode function to add a node.

void List::addNode(Cow newData)
{
    //Creates a Cow object that will skew counters. BELOW.
    node* n = new node;
    n->data = newData;
    n->next = NULL;
    if(head == NULL){
        head = n;
    }else{
        curr = head;
        while(curr->next != NULL){
            curr = curr->next;
        }
        curr->next = n;
    }

}

With the line node* n = new node, it'll create a new Cow object, which calls the Cow constructor, which increments the number of Cows alive static variable.

Simply, my question is... How would I go about not calling the constructor for that Cow object when the node is first created, so I can fill it up with the newData object instead. Therefore not messing up my counter, which increments in the constructor?

Cameron
  • 29
  • 3
  • Why would you not want to increment the _"number of Cows"_ counter. `new node` will create "new" _"`Cows`"_, isn't ? – P0W Mar 12 '17 at 05:21
  • Constructors are always called. You can use more than one constuctor as well. I recommend having an empty constructor that does nothing – Dev2017 Mar 12 '17 at 05:23
  • If the police is not around, you can do some "cheating" and borrow `malloc` from cousin `C` then invoke in-pace construction where needed. Unlike `new`, `malloc` does not invoke the constructor. ;) – A.S.H Mar 12 '17 at 09:30

3 Answers3

2

You could create a node constructor which takes a Cow parameter and uses it to copy-construct its internal Cow. I assume Cow's copy constructor doesn't increment the static counter.

struct node{
    node(Cow &cow): data(cow) {}    

    Cow data;
    node* next;
};
  • And since cows are big, it makes sense to rather `move` them than to copy them. `node(Cow &&cow): data(std::move(cow)){}` – xtofl Mar 12 '17 at 05:33
  • Might be interesting: http://stackoverflow.com/questions/24974935/how-does-stdlist-allocate-nodes-vs-elements – xtofl Mar 12 '17 at 05:35
0

What you may want is moving your object into the list. That way, you won't have more 'live' objects than needed.

You can accomplish this the simplest way by separating the Cow from the Node by e.g. wrapping it inside an std::unique_ptr<Cow>. You get all the correctness for free.

The other way is to only allow moving cows into your list:

List::addNode(Cow &&cow) {
    auto node = new node(std::move(cow));
    ...
}

But then you'll also need to add a move constructor to Cow, which obviously does not increment the counter:

Cow::Cow(Cow &&other)
  : field1(std::move(other.field1))
  , field2(std::move(other.field2))
{ // dont increment counter! 
   other.dontDecrementCounterOnDestruction(); // important!
}

An extra possibility is to actually 'emplace' your cow onto the list (like std::emplace_back()):

template<typename T...>
void List::addNode(T... arguments) {
  auto n = new node(arguments...);
  ...
};

template<typename T...>
node::node(T... arguments): myCow(arguments...)
{ // ... whatever else needs to be done 
}

But then you're getting very close to what std::list does, and unless it's an exercise, you'll better be of just using std::list or std::forward_list anyway.

xtofl
  • 40,723
  • 12
  • 105
  • 192
-1

How would I go about not calling the constructor for that Cow object when the node is first created,

You can't do that. When you construct a node object, all of its member variables will be initialized. With node being defined the way it is and if Cow has a default constructor, then it will be called to initialize data.

so I can fill it up with the newData object instead.

The best you can do is use a move constructor or a copy constructor to avoid the cost of construction followed by assignment.

Update node to:

struct node{

   node() : data(), next(nullptr) {}

   node(Cow const& copy) : data(copy), next(nullptr) {}

        Cow data;
        node* next;
};

and then update addNode to:

void List::addNode(Cow newData)
{
   node* n = new node(newData);
   if(head == NULL){
      head = n;
   }else{
      curr = head;
      while(curr->next != NULL){
         curr = curr->next;
      }
      curr->next = n;
   }
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • "you can't do that". How is it done in `std::list`? – xtofl Mar 12 '17 at 05:34
  • @xtofl, Are you saying that if you use a `std::list` instead of your own list, the constructor doesn't get called when you add an item to the list? – R Sahu Mar 12 '17 at 05:38
  • Almost: the `T` is _allocated_ together with the rest of the node, but is constructed _in place_ afterwards. – xtofl Mar 12 '17 at 05:42
  • You can allocate memory for a `node` using `malloc` and then construct the `T` in place by using a placement `new` operator. It's hard for me to tell whether the standard library you are using is relying on that technique. Regardless, the constructor of `T` will be called when the placement `new` operator gets called. Ultimately, in your case, `data` needs to be initialized as a `Cow` object. If you have user defined constructors, you may use any of them to initialize `data`. But if you have at least one user defined constructor, a constructor has to be called to initialize `data`. – R Sahu Mar 12 '17 at 05:52
  • I'm most known with the MSVC included stl. But regardless, I was negating the assumption that it can't be done. – xtofl Mar 12 '17 at 06:01
  • Using `node* ptr = (node*)malloc(sizeof(node));` is not exactly constructing a `node`. – R Sahu Mar 12 '17 at 06:06