I'm new to C++ and I'm trying to figure out this problem I'm having with my constructor for one of my classes. What happens is... all my variables are initialized properly except two (health and type).
#pragma once
#include <irrlicht.h>
#include <vector>
#include <cassert>
using namespace irr;
using namespace core;
using namespace scene;
enum
{
PLAYER = 0,
NPC = 1,
SOLDIER = 2,
CHAINGUNNER = 3
};
class Model
{
public:
Model(void);
Model(int id, std::vector<ISceneNode*> modelVec, int modType);
~Model(void);
std::vector<int> path;
std::vector<ISceneNode*> model;
int endNode;
int type;
int animate;
int health;
u32 lastAnimation;
private:
int mId;
};
#include "Model.h"
Model::Model(void)
{
//assert(false);
}
Model::Model(int id, std::vector<ISceneNode*> modelVec, int modType)
{
path = std::vector<int>();
model = modelVec;
endNode = 0;
type = modType;
animate = 0;
health = 100;
lastAnimation = 0;
mId = id;
}
Model::~Model(void)
{}
I create a model with Model soldier(id, model, SOLDIER)
Everything is set properly except type and health. I've tried many different things, but I cannot figure out my problem. I'm not sure but the default constructor is being called. It doesn't make sense because I make no called to that constructor.
Thanks,
vector<ISceneNode*> model;
model.push_back(soldierBody);
model.push_back(soldierHead);
model.push_back(soldierWeapon);
cout << "Id of char: " << id << endl;
Model soldier(id, model, SOLDIER);
modelMap[id] = soldier;
This lines:
modelMap[id] = soldier;
First default constructs the Model inside the map.
The returned reference is then used with the assignment operator to copy the value of soldier into the value contained inside the map.
To test if it is working try:
Model soldier(id, model, SOLDIER);
std::cout << "TYPE(" << soldier.type << ") HEALTH(" << soldier.health << ")" std::endl;
modelMap[id] = soldier;
std::cout << "TYPE(" << modelMap[id].type << " HEALTH(" << modelMap[id].health << ")" std::endl;
If your class is not designed to be default constructible.
Then do not have a default constructor (this will just lead to problems).
Declare a default constructor in the private part of the class (no need for a body).
Without a default constructor you will not be able to use the operator[] on map. But you can get around this by using insert:
modelMap.insert(std::map<XX, Model>::value_type(id, soldier));
From the comments, you say that you are inserting these into a map like so:
modelMap[id] = Model(id, model, SOLDIER);
std::map::operator[] requires that the mapped type be default constructible. When you call operator[] on a map, if there is no mapped value with the given key, the map default constructs a new object, maps it to the given key, and returns a reference to that object.
You can get around this by using std::map::insert():
modelMap.insert(std::make_pair(id, Model(id, model, SOLDIER));
You do:
Model soldier(id, model, SOLDIER); //1
modelMap[id] = soldier; //2
What happens here?
1. New object is created, using consructor you have provided.
2. The so-called copy-constructor copy assignment operator is called to copy soldier to modelMap[id]. You haven't defined your own copy-constructor copy assignment operator so one default is created for you by compiler, it is in most cases just copying byte-by-byte whole data-structure to the new memory address. However you have vector of pointers in your class, so compiler should call copy-constructor of vector... And I don't know (maybe someone with greater experience would know exactly what happens now) what is the result copy-constructor, I don't know if standard clearly defines 'default copy-constructor'.
So it is possible, that the whole structure is copied to the modelMap[] but with some random data.
If you create a copy-constructor (its declaration in your case will look something like Model::Model(const Model& myModel);, copy-constructor always takes reference to object of its type as an argument) If you override copy assignment operator (best, if you make both things), you have control over everything that is done while copying your object to another variable/object.
Download eg. Bruce Eckel's Thinking in C++, V. 1 [1], or search somewhere on the Net how to do it (probably this will be good, didn't read whole article, http://www.learncpp.com/cpp-tutorial/911-the-copy-constructor-and-overloading-the-assignment-operator/).
[1] Downloadable on his website, mindview.net, as a new user I can paste only one link, so cannot link it here myself :P.
Related
So, I've been exploring on how to create a dynamical array with a custom template class that I made.
#include <iostream>
#include <vector>
//HOW TO SET CLASS INTO DYNAMICAL ARRAY WITH VECTOR
//CREATE A CLASS
class User{
std::string name;
public:
User(){
}
User(std::string name){
this->name = name;
}
void set_name(std::string name){
this->name = name;
}
std::string get_name(){
return name;
}
};
int main(){
//SET A NORMAL ARRAY THAT CAN CONTAIN AN OBJECT
User user[1];
//DO WHATEVER WITH THE USER[0] TO SET EVERYTHING THAT LATER WILL BE PUT IN VECTOR
user[0].set_name("Meilianto");
std::cout << "user[0]: " << user[0].get_name() << std::endl;
//CREATE A DYNAMICAL ARRAY WHICH IS VECTOR
std::vector<User> vuser;
//PUSHBACK TO THE VECTOR AS "FIRST ELEMENT" BY PUTTING "USER[0]" AS AN ARGUMENT
vuser.push_back(user[0]);
std::cout << "vuser[0]: " << vuser[0].get_name() << std::endl;
//YOU CAN "MODIFIED" THE "USER[0]" AND ADD AGAIN AS THE "SECOND ELEMENT" OF VECTOR
user[0].set_name("Meilianto1");
vuser.push_back(user[0]);
std::cout << "vuser[1]: " << vuser[1].get_name() << std::endl;
//YOU CAN EVEN "MODIFIED" THE "FIRST ELEMENT" BY CALLING THE "METHOD" OF IT
vuser[0].set_name("Hantu");
std::cout << "vuser[0]: " << vuser[0].get_name() << std::endl;
//THE QUESTION HERE, CAN I DECLARE ARRAY TOGETHER WITH THE CONSTRUCTOR?
User user1[1]("Bebek");
//AND AFTER THAT I CAN ADD THAT OBJECT STRAIGHT AWAY TO VECTOR WITHOUT ASSIGNING ALL THE
//MEMBERS ONE BY ONE
return 0;
}
If you have read my comments in my code, what I am trying to do is maybe it will be faster if I just construct right away when I create the object instead of assigning all the members one by one that will cost more code. I imagine if in the future there will be an object with a lot of members and need to assign it one by one. It won't be efficient.
EDIT: I edit the User user[0] into User user[1], Thanks
If you're using a modern standard of C++, then you can do this
std::vector<User> vuser {
{"Meilianto1"},
{"Hantu"},
{"Bebek"}
};
Where each pair of inner brackets with a string calls User constructor, and outer pair of brackets calls std::vector<User> constructor with a sequence of Users
THE QUESTION HERE, CAN I DECLARE ARRAY TOGETHER WITH THE CONSTRUCTOR ?
User user1[1]("Bebek");
You can use list initialization for that, for arrays as well as for vectors:
User users[] { std::string("Herbert"), std::string("Anton") };
std::vector<User> vusers { std::string("Herbert"), std::string("Anton") };
CAN I ADD THAT OBJECT STRAIGHT AWAY TO VECTOR WITHOUT ASSIGNING ALL THE MEMBERS ONE BY ONE
You can initialize a vector with the elements of an previously defined array like this:
std::vector<User> v2users(std::cbegin(users), std::cend(users));
BTW: note that User user[0]; in your code defines an array without elements, i.e. of size 0, which rarely makes sense. Accessing user[0] leads to undefined behaviour
Yes, you can!
User users[]{ User{ "one" }, User{ "two" } };
// Construct vector from iterator-pair:
std::vector<User> users_vector{ std::cbegin(users), std::cend(users) };
You can use emplace_back method or push_back with temporary object to add the vector. For example;
vuser.emplace_back("Meilianto1"); // 1
vuser.push_back(User{"Meilianto1"}); // 2
The first one passes the arguments to the constructor. The second one will move the variable to vector. To be honest, there will be copy elision that's why there will be no move or copy. Just it will construct the variable.
Consider the following snippet:
#include <memory>
#include <typeinfo>
#include <iostream>
class Widget {
};
int main() {
auto shared_ptr_to_widget = std::shared_ptr<Widget>({});
std::cout << "type of shared_ptr_to_widget: " << typeid(shared_ptr_to_widget).name() << std::endl;
auto maybe_a_widget = *shared_ptr_to_widget;
std::cout << "type of maybe_a_widget: " << typeid(maybe_a_widget).name() << std::endl;
}
This will output:
> type of shared_ptr_to_widget: St10shared_ptrI6WidgetE
> type of maybe_a_widget: 6Widget
However, if I replace the Widget class with:
class Widget {
public:
Widget(): a{1}{}
int a;
};
Then it seg faults at the following line:
auto maybe_a_widget = *shared_ptr_to_widget;
I understand if I actually wanted to make a shared pointer to an instance of an object I should use
std::make_shared<Widget>()
and this would actually call the constructor of Widget and everything would be fine and dandy. But I'd really like to understand what is going on here and why the behaviour changes based on the constructor of the Widget class.
I've tried looking at the constructors for shared_ptr http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr but I get a bit lost. I imagine it's my lack of understanding in what is actually happening when using an empty initializer list as a parameter to shared_ptr<>().
Your code has undefined behaviour, since *shared_ptr_to_widget dereferences a null pointer, since you only default-constructed shared_ptr_to_widget, which results in an empty shared pointer that doesn't own anything.
At the point where the program execution has undefined behaviour, the C++ standard imposes no constraints on the behaviour of a conforming implementation, so anything can happen. You are observing a particular instance of "anything".
To create a pointer that owns a widget, say either
auto shared_ptr_to_widget = std::shared_ptr<Widget>(new Widget); // bad
or
auto shared_ptr_to_widget = std::make_shared<Widget>(); // good
Take a look at this code, it has a class that, when a new object is created, will give it a random number for 'lvl' between 1 and 100. After the class, I define some objects using class instances.
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
class newPokemon {
public:
int lvl;
newPokemon() {
lvl = (rand() % 100 + 1);
};
void getLevel() {
cout << lvl << endl;
};
};
newPokemon Gengar;
newPokemon Ghastly;
newPokemon ayylmao;
};
What I want to do next is allow the use to define new pokemon (objects) by asking them for a name. This means, however, I need to create objects dynamically. For example,
Program asks user for a name
Name is then saved as an object from the class newPokemon
Program can use that name to run other functions from the class, like getLevel.
How am I able to do this? I, of course, get that I can't do it like the hard coded ones, as I cannot reference user input as a variable name, but is there some way to do what I am asking by manipulating pointers or something?
Use std::map to hold your objects according to their names:
std::map<std::string, newPokemon> world;
You must make sure that your objects get added to the map immediately after being created.
std::string name;
... // ask the user for a name
world[name] = newPokemon();
std::cout << "Your level is " << world[name].getLevel() << '\n';
You probably just want each Pokemon to have a name property (member variable/field). Just make a bunch of Pokemon with the name filled in.
I have a homework problem which I don't quite understand.
Add three constructors to the class Critter. Each constructor should
also print a simple informational message on the screen such that one
can see when and which constructor has been called. You should be able
to create an instance of the Critter class 1) without supplying any
properties (which should set the name to “default critter”, the height
to 5 and the rest to 0), 2) by only supplying a name as parameter
(which should set the height to 5 and the rest to 0), and also 3) by
supplying name, hunger, boredom and height all as parameters. You
should also be able to create an instance of the Critter class without
specifying the height. If the height is not supplied, the critter has
the default height of 10. Write a test program which creates four
instances of the Critter by using these three different constructors
(the last one in two ways). Set their hunger levels to 2 by using
appropriate method and/or constructor calls. The critters’ properties
should then be printed on the screen.
Okay, so first I created a class Critter then I added 3 constructors in it as it is described in the points 1, 2 and 3. Then I created an object or instance(they are the same thing right?). After that I created another object and created another constructor. The problem is, I am lost at the last 3 sentences:
Write a test program which creates four
instances of the Critter by using these three different constructors
(the last one in two ways). Set their hunger levels to 2 by using
appropriate method and/or constructor calls. The critters’ properties
should then be printed on the screen.
How do I create 4 instances of the Critter by using those three different constructors?
This may sound like a dumb question, but I have never worked before with classes. I am a fan of procedural programming.
This is my code:
Critter.h
class Critter {
// The following data members are private
private:
std::string name;
int hunger, boredom;
double height;
public:
Critter();
Critter(std::string& newname);
Critter(std::string& newname, int newhunger, int newboredom, double newheight);
Critter(std::string& newname, int newhunger, int newboredom);
};
and in another file I wrote:
Critter.cpp
#include <iostream>
#include "Critter.h"
using namespace std;
Critter::Critter() {
name = "default critter";
height = 5.0;
hunger = 0;
boredom = 0;
cout << "First with no properties." << endl;
}
Critter::Critter(string& newname) {
name = newname;
height = 5.0;
hunger = 0;
boredom = 0;
cout << "Only name as a parameter." << endl;
}
Critter::Critter(string& newname, int newhunger, int newboredom, double newheight) {
name = newname;
height = newheight;
hunger = newhunger;
boredom = newboredom;
cout << "All as parameters." << endl;
}
Critter::Critter(string& newname, int newhunger, int newboredom) {
name = newname;
height = 10.0;
hunger = newhunger;
boredom = newboredom;
cout << "All as parameters." << endl;
}
and the main file:
#include <iostream>
#include "Critter.h"
using namespace std;
int main() {
Critter first_instance, second_instance;
string name;
int hunger, boredom;
double height;
return 0;
}
Looking forward to your suggestions/answers.
Thank you
You have created 4 constructors, but they want you to have 3, the last of which should be used in 2 ways. The first two are fine, what they want you to do is to merge your third and fourth constructor into a single one by using default arguments for functions. Like this:
Critter::Critter(string& newname, int newhunger, int newboredom, double newheight = 10.0) {
name = newname;
height = newheight;
hunger = newhunger;
boredom = newboredom;
cout << "All as parameters." << endl;
}
This way it's a single constructor, but you can call it in two ways:
either with all the arguments, including newheight, or
by providing only the first three, and then newheight will have the
default value of 10.0.
EDIT to answer the question in a comment:
How do you create 4 instances using these 3 constructors?
The line
Critter my_first_critter;
will create an object of class Critter called my_first_critter, with no arguments, which means that the compiler will select the first constructor, and you'll read "First with no properties.". Instead, a line like
Critter my_second_critter("John");
will create a new object of class Critter, called my_second_critter, and since there's a string argument the compiler will select the second constructor, so that you'll read "Only name as a parameter.". Then, a line like
Critter my_third_critter("James", 2, 3, 5.5);
(notice the fourth argument!) will create one more object, again of class Critter and called my_third_critter, and since there are four arguments, of type: string, int, int, double, the compiler will call the third constructor and you'll read "All as parameters.". Finally, a line like
Critter my_fourth_critter("Peter", 2, 3);
(notice the fourth argument is missing!) will call the same function, because in this case the default argument kicks in. The compiler will still match this call to the third constructor (so that you will read, again, "All as parameters."), but in this case the height will be the default value of 10.0, as the programmer that created the object (you) didn't specify the value.
Setting the default value of height to 10.0 is useful if it happens very often that the height is 10.0 and you don't want to waste time specifying this number every time you create a Critter (which would also be error-prone: think of typos or getting confused...). Still, if you need a value other than 10.0, you can easily provide it. And since it's just one constructor, and not 2 separate ones, you have less code to manage. You could, of course, avoid using default arguments and write 2 constructors (as you have done), but suppose you find a bug in your third one: most certainly you'd have to fix it in your fourth one as well. What if you forget to do it? If you have just one function your code is easier to manage. Also, consider that you can provide more than one default argument per function. If you had to do without default arguments, you'd have to create a lot of copies. It wouldn't scale well.
i am having trouble with my code. I am abit stumped.
I have a data member which is a pointer to a string type.
I use the constructor as a defualt initialer to this pointer, then when I call an object in the main function the intialised pointer to points to the memory address where the string is stored and prints the contents. That is what is supposed to happen, but I can't get the program to work. May somebody please tell me where I am going wrong?
#include<iostream>
#include<string>
using namespace std;
class NoName{
public:
NoName(string &sName("Alice In Wonderland") ){};
private:
string *pstring;
};
int main(){
//the constructor will be automatically called here once a object is created
// and the string "Alice in Wonderland" will appear on the screen
return 0;
}
Just simply use a std::string member and initialize it in Member initializer list:
private:
string mstring;
public:
NoName():mstring("Alice In Wonderland"){}
You could also let the constructor take in a parameter instead of hardcoding the string and let the user pass the string at run-time:
NoName(std::string str):mstring(str){}
You do not need a pointer. By using a pointer to std::string You nullify the advantages of implicit manual memory management offered by std::string.
If you really need to store a pointer for some reason, then there are some points to remember:
Pointers are initialized like new Class
Prefer to initialize class members in the member initializer list
Any time you write the word new think about where you're going to write delete. (In this case it goes in the destructor.
Rule of Three: If you need a destructor (you do, because of delete), then you also need a copy constructor and copy assignment operator.
This is one way your code could look: http://ideone.com/21yGgC
#include<iostream>
#include<string>
using std::cout; using std::endl;
using std::string;
class NoName
{
public:
NoName(string sName = "Alice In Wonderland") :
pstring(new string(sName))
{
cout << "ctor - " << *pstring << endl;
}
NoName(const NoName& rhs) :
pstring(new string(*rhs.pstring))
{
cout << "Copy ctor - " << *pstring << endl;
}
NoName& operator=(const NoName& rhs)
{
*pstring = *rhs.pstring;
cout << "Copy assignment operator - " << *pstring << endl;
return *this;
}
~NoName()
{
cout << "dtor, my name was " << *pstring << endl;
delete pstring;
}
private:
string *pstring;
};
.
int main()
{
NoName m, n("Another name");
NoName o(m);
o = n;
return 0;
}
Notice how much easier it is if you don't use the unnecessary pointer:
class Better
{
public:
Better(string sName = "Alice In Wonderland") :
m_string(sName)
{
}
private:
string m_string;
};
Because you don't need the custom destructor, you also don't need the copy constructor or copy assigment operator either. Much easier!
You're not using the constructor properly. First of all, you create this reference parameter and try to initialize it to a string object (that's asking for problems). Second, your constructor never actually does anything.
You need to call new on your pointer, dereference it and give the data pointed to a value, output the dereferenced value with std::cout and then clean the memory up with delete in the destructor (or in this case, you can do it after you use cout if you're not planning on using that string again. But do it in the destructor if you need it still).
Assuming you're doing this for a class, your textbook should tell you how to do these things.
EDIT: this is also not the default-constructor. I changed your tag to match appropriately.