If you have a generic Node that store ints, float or Objects of a certain type, how could you store generic objects in your node?
typedef struct node{
Dog data;
node* next;
}*nodePtr;
This node stores Dog objects... how could I store generic objects?
One idea I have is to have Dog objects and all other objects inherit from a more general Object class. Good way to go other than using templates?
C++ offers the template<> for generics:
template<typename T>
struct node {
T data;
node<T> *next;
}
Make a template, like this:
template<typename T>
struct Node
{
T data;
Node<T> *next;
};
A good resource to find information on templates can be e.g. the Wikipedia.
One idea I have is to have Dog objects and all other objects inherit from a more general Object class. Good way to go?
If the types all have something in common, create a common base type for them. If not, then don't.
Don't make the types derive from a common base just because you want to store them all in the same container. You'd have it backwards. If you want to store all the types in the same container, they should have something in common already. Otherwise your container is just a sequence of bits. There would be nothing it could do that wouldn't be better done by separate containers for each type. For example, you couldn't iterate through the container and call a method on each element, because there wouldn't be a method that all the elements have!
You said,
Great answer, but I'm looking to do it through OO principles.
One of the basic principles of OO, IMO, is that all your classes should be meaningful. This doesn't mean they have to correspond to concrete objects, or even contain any implementation, but they do have to at least contain some interface. A generic Object class in C++ is not meaningful. Don't create one.
Related
Suppose you have the following class:
template<typename T>
class Node {
public:
T data;
std::vector<Node*> children;
};
I excluded its methods because I want to focus on a different point: the developer is constrained to use std::vector as a container for children.
In the context of programming to an abstraction, I'd like to learn how to make std::vector easily interchangeable with:
another container from the STL
an abstract class, with its own set of methods (potentially something that could become, or fulfilled with, a std::vector, std::map, std::list, etc.)
What ways do you know to accomplish this goal, and what are their main advantages and disadvantages?
Would C++20's Concepts help with this task? If so, I'd like to learn both the old and the new way.
You could add an additional template template parameter, like this:
template<template <typename...> typename Container, typename T>
class Node {
public:
T data;
Container<Node*> children;
};
and then instantiate Node with different containers:
Node<std::vector, int> n;
Node<std::set, int> m;
However, if you want to do something like
Node<std::map, int> m;
you will run into issues because map needs at least 2 template parameters, and you are only using Node*.
Before trying out any particular implementation strategy, I suggest you think about how you want to use the Node class, since this will inform the implementation.
So I am in process of coding class for some graph structure (suffix tree), and I encountered this difficult thing: a node should have pointers, one pointing on its parent, and also a set of pointers to its sons. I am using smart pointers to avoid errors with memory, and here is the thing: I write
class node {
...
std::shared_ptr<node> parent;
...
};
I guess there is a term for this thing. Like self-referencing, or whatever. When I wrote it, initialy i was confident, that there will be an error, but no it had been successfully compiled. I was curious, can i write something like that:
class some_class_2{
...
std::vector<some_class_2> some_vector;
std::string string_;
some_class() {
string_ = "lol";
some_vector = std::vector<some_class>(10);
}
};
Its appeared that the answer on this question is yes. So when i launched the test programm which had been succesfully compiled, of course it wouldn't stop, maybe i waited not enough and it should throw me some memory related error.
So the question is, how you should handle with this kind of things in c++? Isn't it strange that those things allowed in c++? Is it normal to put std::shatre_ptr inside some_class? How to write safe code, where errors like in some_class_2 are avoided, and what is the best way to
represent graph structure?
If the language did not allow any use of node inside the definition of node, then there would be no way to create linked data structures such as lists and trees. In order for the language to be useful, it has to allow such constructs as:
struct node {
int key;
node* next;
};
On the other hand, the language cannot allow this:
struct node {
int key;
node next;
};
because then a node object would contain an infinite nested sequence of node objects and be infinitely large.
The way the language deals with this is to allow members of classes only to be complete types. A complete type's size is known. A class type is not complete until its full definition has been seen. So node is not complete inside its own definition, so you cannot put a node member inside a node object. But node* is complete even when node is not complete yet.
Some standard library templates can be used with complete types. The smart pointers, std::unique_ptr<T>, std::shared_ptr<T>, and std::weak_ptr<T>, are allowed to have incomplete T, since they have pointer semantics. However, std::vector does not allow its template parameter to be incomplete, and the result is undefined behaviour. The way to avoid this is to just not write it (it's not that hard).
I'm working on a class hierachy, where an object of a class may contain several objects of the same class.
This would lead to a tree structure:
class myClass
{
myClass *parent;
std::vector<std::unique_ptr<myClass> > childs;
/*
* here are some more attributes which describe a myClass object
* but are not related to the tree structure.
* std::string name; for example
*/
public:
//constructors...
//tree management functions
//some more members...
}
However, after thinking about it for a while, i think this is bad practise.
In this approach, myClass not only needs to have members related to its actual behaviour but also for managing the tree, like searching or inserting childs. This would be a lot of different functionality in one single class.
And, as we are lazy programmers, I don't like to re-invent the wheel.
There are some tree-containers out there, for example the well-known "tree.hpp".
Why not using this container for storing the myClass objects?
Well, problem is, some members ob myClass require access to its parents.
Imagine a member-function like getFullName(), which returns not only the "name"-attribute but a complete path to the actual object(all the parent's names). So this function would need to iterate trough all parent nodes until root is reached.
I'm not sure how i can achieve this using tree.hpp or similar containers.
Does myClass then need to store a pointer to the tree-node which contains it?
But I cannot think of an example where an object has information about the container containing it. An object of a class should not know anything of "being contained". Or am I wrong?
Maybe my first approach (myClass does also the tree management) is even OK?
OK, maybe i should ask a simplier question:
What is a good way to let an object know its own position within a container, e.g. a tree?
What is a good way to let an object access its parent in without storing to much (redundant) information in the object itself?
First of all, this is wrong:
class myClass
{
myClass *parent;
std::vector<std::unique_ptr<myClass> > childs;
};
Your children should be stored as a vector of shared_ptr, and the parent should be a weak_ptr.
class myClass
{
std::sweak_ptr<myClass> parent;
std::vector<std::shared_ptr<myClass> > children;
};
However, it looks like what you want is a template tree container. So your class should look like this:
class myClass
{
/*
* here are some more attributes which describe a myClass object
* but are not related to the tree structure.
* std::string name; for example
*/
public:
//constructors...
//tree management functions
//some more members...
};
And the tree container should be something like this (conceptually):
template <typename T>
class tree
{
std::sweak_ptr<tree> parent;
std::vector<std::shared_ptr<tree> > children;
T value;
};
This tree can be generic and hold different types of objects, including myClass.
I am reading one of the books and stuck at one particular question.
Definition of a struct for linked list :::
typedef struct LinkedList{
LinkedList* next;
int data;
}
Book says "Placing the next pointer at the beginning of the structure or class makes it easy to write generic list-handling routines no matter what the data holds."
I am not able to understand how placing the next pointer on top will help here.
Also, to make a generic list, wouldn't we need the data type as generic or say void*?
The book you're looking at, Programming Interviews Exposed, is (as far as I can tell) not a book on C++, but rather a book meant to prepare you to answer the kinds of questions that might be asked at a typical technical interview. I wouldn't take anything in the book as a best C++ practice unless it's labelled as such (and perhaps not even then).
The advice to put the next pointer first in a linked list node structure comes from languages like C, where you can't rely on real, compiler-supported inheritance. The idea is, in fact, to implement something like inheritance yourself by piggybacking data onto the linked list node structure. Consider:
typedef struct LinkedList {
LinkedListNode* next;
int type;
}
typedef struct Person {
LinkedList listNode;
char name[64];
int age;
}
typedef struct Address {
LinkedList listNode;
char streetAddress[128];
char city[32];
char state[2];
char zip[10];
}
typedef struct Employee {
Person person;
int department;
int salary;
}
LinkedList here is a base type -- not good for much by itself, but useful as a starting point for nodes with more data. You don't have to know anything about the other types in order to perform linked list operations on a node... you can cast any node pointer to LinkedList* and access the information you need. So, you can have a list of Person and list of Address, and both can be manipulated with the same set of routines. Likewise, you can cast a Employee* to a Person* and use any operations that you've written for Person on Employee as well. If you assign appropriate constants to LinkedList's type field, you can even mix PersonNode and use the type field to determine the type of each node later.
This was a useful way to program 20+ years ago. It still works, of course, but most people would choose to let the compiler manage inheritance for them if they have the option, and all modern object-oriented langauges do offer that option.
Lesson: Understand the technique in case you come across it in old code, but choose a different implementation for your new code if you can.
If you place the next pointer at the beginning, and all you have is a void*, but you know it's a linked list node, you can always find the next pointer. This is in contrast to the next pointer being placed after the 'data', assuming 'data' can be of different sizes, you'd need to know more about the object in order to find the next pointer
I personally do not see any reason, and I agree, the argument should be templated:
template <class T> class ListElement
{
T data;
ListElement* next;
...
}
There may be some issues regarding speed with memory alignment being a bit better the other way round, but I doubt this!
hth
Mario
I have C# background and been working with C# for so many years.. Recently, I'm learning C++ and having some difficulties..
Basically, I'm trying to create the linked link class as below. I want to use my class as a data in struct node.
How can I fix this in C++? Thanks.
But it said that i can't use like that.
class Polynomial{
public:
Polynomial(pair<double, int>);
void add(Polynomial);
Polynomial multiply(Polynomial);
void print();
private:
struct node
{
Polynomial data;
node *link;
}*p;
};
Your node struct contains a member variable of type Polynominal, but since node itself is declared inside Polynominal, the declaration of Polynominal isn't complete at that point.
I get the impression that you assume classes in C++ to work just like C#, but they don't. C++ isn't garbage-collected, and it doesn't automatically manage references for you when you use classes. A class in C++ behaves more like a struct in C#, and when you pass or declare it like in your example, it gets copied by value.
Another thing: C++ comes with STL, which contains a range of templates for all sorts of things, including a nice linked list (std::list).
Couple of issues:
Polynomial doesn't have a default constructor, so the only way to create it is by using that custom constructor you have. However, your inner struct contains an object of type Polynomial. How is that supposed to be created? You can't embed objects that don't have a default constructor in classes unless you initialize them specifically in the container's constructor.
Your struct contains an object of the type of the parent class, which you're still in the process of defining! If anything, you need to make that struct its own class.
In general, you seem to do a lot by-value operations. This is very inefficient - you should always pass Polynomial by reference or pointer.
To fix it just use Polynomial &data; instead of Polynomial data; in the struct
Change that to Polynomial *data; and it will work just fine. And therein lies your clue as to what's wrong. Understanding that will bring great enlightenment.
One way of explaining it is that in C++ (unlike C#) a Polynomial and a float behave in exactly the same way with regards to how storage is allocated with them. In C# you can't do new float; (not to be confused with new Float();) and in C++ you can.
The points raised by EboMike are all valid, but just to make it compile (it's still unusable due to the constructability issue):
class Polynomial{
public:
Polynomial(pair<double, int>);
void add(Polynomial);
Polynomial multiply(Polynomial);
void print();
private:
struct node; // forward declaration creates incomplete type
node *p; // OK to have pointer to incomplete type
};
struct Polynomial::node
{
Polynomial data; // class Polynomial is complete now
node *link;
};