Need help creating an array of objects - c++

I am trying to create an array of class objects taking an integer argument. I cannot see what is wrong with this simple little code. Could someone help?
#include <fstream>
#include <iostream>
using namespace std;
typedef class Object
{
int var;
public:
Object(const int& varin) : var(varin) {}
} Object;
int main (int argc, char * const argv[])
{
for(int i = 0; i < 10; i++)
{
Object o(i)[100];
}
return 0;
}

In C++ you don't need typedefs for classes and structs. So:
class Object
{
int var;
public:
Object(const int& varin) : var(varin) {}
};
Also, descriptive names are always preferrable, Object is much abused.
int main (int argc, char * const argv[])
{
int var = 1;
Object obj_array[10]; // would work if Object has a trivial ctor
return 0;
}
Otherwise, in your case:
int main (int argc, char * const argv[])
{
int var = 1;
Object init(var);
Object obj_array[10] = { var, ..., var }; // initialize manually
return 0;
}
Though, really you should look for vector
#include <vector>
int main (int argc, char * const argv[])
{
int var = 1;
vector<Object> obj_vector(10, var); // initialize 10 objects with var value
return 0;
}

dirkgently's rundown is fairly accurate representation of arrays of items in C++, but where he is initializing all the items in the array with the same value it looks like you are trying to initialize each with a distinct value.
To answer your question, creating an array of objects that take an int constructor parameter. You can't, objects are created when the array is allocated and in the absence of a trivial constructor your compiler will complain. You can however initialize an array of pointers to your object but you really get a lot more flexibility with a vector so my following examples will use std::vector.
You will need to initialize each of the object separately if you want each Object to have a distinct value, you can do this one of two ways; on the stack, or on the heap. Lets look at on-the-stack first.
Any constructor that take a single argument and is not marked as explicit can be used as an implicit constructor. This means that any place where an object of that type is expected you can instead use an instance of the single parameter type. In this example we create a vector of your Object class and add 100 Objects to it (push_back adds items to a vector), we pass an integer into push_back which implicitly creates an Object passing in the integer.
#include <vector>
int main() {
std::vector<Object> v;
for(int i = 0; i < 100; i++) {
v.push_back(i);
}
}
Or to be explicit about it:
#include <vector>
int main() {
std::vector<Object> v;
for(int i = 0; i < 100; i++) {
v.push_back(Object(i));
}
}
In these examples, all of the Object objects are allocated on the stack in the scope of the for loop, so a copy happens when the object is pushed into the vector. Copying a large number of objects can cause some performance issues especially if your object is expensive to copy.
One way to get around this performance issue is to allocate the objects on the heap and store pointers to the objects in your vector:
#include <vector>
int main() {
std::vector<Object*> v;
for(int i = 0; i < 100; i++) {
v.push_back(new Object(i));
}
for(int i = 0; i < 100; i++) {
delete v[i];
}
}
Since our objects were created on the heap we need to make sure that we delete them to call their deconstructor and, free their memory, this code does that in the second loop.
Manually calling delete has it's own caveats, if you pass these pointers to other code you can quickly loose track of who owns the pointers and who should delete them. An easier way to solve this problem is to use a smart pointer to track the lifetime of the pointer, see either boost::shared_ptr or tr1::shared_ptr which are reference-counted pointers :
#include <vector>
int main() {
std::vector<shared_ptr<Object> > v;
for(int i = 0; i < 100; i++) {
Object* o = new Object(i);
v.push_back(shared_ptr<Object>(o));
}
}
You'll notice that the shared_ptr constructor is explicit, this is done intentionally to make sure that the developer is intentionally stuffing their pointer into the shared pointer. When all references to an object are released the object will automatically be deleted by the shared_ptr, freeing us of the need to worry about it's lifetime.

If you want to stick to arrays, then you must either initialize manually or use the default constructor. However, you can get some control by creating a constructor with a default argument. This will be treated as a default constructor by the compiler. For example, the following code prints out the numbers 0, ..., 9 in order. (However, I'm not sure that the standard dictates that the objects in the array must be constructed in order. It might be implementation dependent, in which case the numbers may appear in arbitrary order.)
#include <iostream>
using namespace std;
struct A {
int _val;
A(int val = initializer()) : _val(val) {}
static int initializer() { static int v = 0; return v++; }
};
int main()
{
A a[10];
for(int i = 0; i < 10; i++)
cout << a[i]._val << endl;
}

Related

Class member value overwritten by previous member

If I create a two class members of char* type and is array. And if I resize first member of the class it overwrite the second member with same values.
Why it happens?
Is it some memory management issues?
#include <iostream>
using namespace std;
class myclass
{
private:
const char *arr1[0];
const char *arr2[4] {
"one",
"two",
"three",
"four"
};
public:
void run() {
int f = 0;
*this->arr1 = new char[4];
for(f = 0; f < 4; f++) {
this->arr1[f] = "foo";
}
for(f = 0; f < 4; f++) {
cout << this->arr2[f] << endl;
}
}
};
int main()
{
myclass *my = new myclass();
my->run();
return 0;
}
Output
foo
foo
foo
foo
const char *arr1[0];
Zero-sized arrays are not allowed in standard C++. Your compiler is allowing it as an extension to the language.
Even if your compiler has this extension, dereferencing an array of size 0 causes undefined behavior and you are doing this here:
*this->arr1 = new char[4];
I don't know what your intention here is, either you want
const char *arr1[4];
in which case *this->arr1 = new char[4]; is unnecessary or you want
const char **arr1;
in which case it should be this->arr1 = new const char*[4];.
You should not use char* to manage strings, use std::string instead, which does the memory management for you. Similarly, for multiple strings use std::vector<std::string> instead of char**.
There doesn't seem any reason to use dynamic memory allocation in main either. The same way as you should use std::vector to manage dynamically-sized arrays of objects instead of using new[]/delete[], don't use dynamic memory allocation to create single objects if you don't have a good reason for it and if you have to, use std::unique_ptr instead of raw new/delete.
int main()
{
myclass my;
my.run();
return 0;
}
This does the same without dynamic allocation.

Array Length, why can't it be used when taking from command lines?

I'm trying to get one of my argv's (which is an int sent in as a string), to become the length of an array, but the compiler keeps telling at me that num_of _players isn't constant and cant be used.
Any suggestions why?
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
int num_of_players = stoi(argv[argc - 1]);
int player_list[num_of_players];
return 0;
}
The problem is that declaring arrays in that way requires the size to be provided at compile time. They can not be declared with a value discovered at runtime.
The most basic alternative is to use a dynamic array which you can create manually:
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
int num_of_players = stoi(argv[argc - 1]);
int* player_list = new int[num_of_players];
// do stuff with player_list here...
delete[] player_list; // REMEMBER to delete[] it!!
return 0;
}
When you create something (like an array) using new or new[] you have to manually dispose of them (incl. give the memory back) using delete or delete[] respectively. However this can be forgotten so it is a potential source of memory leaks. For that reason one would typically use a type that manages the dynamic array automagically like a std::vector.
Because it isn't constant and can't be used.
Like how the size of an int is baked into your program, so is the size of an array. It has 3 elements, or 9 elements, or 512 elements … but this information cannot come from the command-line because it already had to be baked into your program when you compiled it.
Instead, use a "dynamic array" like std::vector that can grow and shrink as needed at runtime.
(Don't use new & you will not forget to delete. Use smart pointers.)
.
As it already written: Because it is not a compile-time known value. This might satisfy your needs:
#include <string>
void fu(size_t num_of_players)
{
std::unique_ptr<int[]> player_list = std::make_unique<int[]>(num_of_players);
player_list[4] = 10;
int x = player_list[4]; // In this example: x = 10
}
int main()
{
fu(10);
}
.
Or, let's say there is a class for "Game":
#include <string>
class CGame // Assume that m_player_list values are not negative
{
public:
CGame(size_t num_of_players) // Constructor
{
m_num_of_players = num_of_players;
m_player_list = std::make_unique<int[]>((num_of_players));
}
int set_player(size_t i, int n)
{
if (i < m_num_of_players) {
m_player_list[i] = n;
return m_player_list[i];
}
return -1; // Out of bounds error
}
int get_player(size_t i)
{
if (i < m_num_of_players)
return m_player_list[i];
return -1; // Out of bounds error
}
protected:
std::unique_ptr<int[]> m_player_list;
size_t m_num_of_players = 0;
};
int main()
{
CGame Game(10); // User input: 10 players
Game.set_player(4, 10);
int x = Game.get_player(4); // In this example: x = 10
return 0;
}

How do I return an array of pointers in a function?

So I have an array of pointers that references 3 instances of a class, I need to create a function that gets the references to those 3 instances and returns it into that array.
Here is what I have been trying:
#include<cstdlib>
#include<cinttypes>
#include<random>
//Random number generator
uint8_t rand(uint8_t max){
std::default_random_engine generator;
std::uniform_int_distribution<uint8_t> distribution(0,max);
return distribution(generator);
}
class MyClass{
//...
}
myClass[100];
MyClass * getReferences(){ //What should the type of this be?
MyClass * arrayOfPointers[3];
for(uint8_t i=0;i<2;++i){
arrayOfPointers[i]=&myClass[rand(2)];
}
return arrayOfPointers;
}
int main(){
MyClass * arrayOfPointers[3]=getReferences();
return EXIT_SUCCESS;
}
As mentioned you are returning a pointer to a local variable which is incorrect.
Use standard library containers to avoid the pitfalls and woes of C-style arrays.
std::array<MyClass *, 3> getReferences()
{
std::array<MyClass *, 3> arrayOfPointers;
for(int i=0; i < 2; ++i) // don't use tiny int types of small for loops. it's not faster and it's harder to maintain
{
arrayOfPointers[i] = &myClass[rand(2)];
}
return arrayOfPointers;
}
int main()
{
std::array<MyClass *, 3> arrayOfPointers = getReferences();
}
Returning a pointer to a local variable is incorrect in C++. It can lead in memory violation errors. If you want to return an array from the function you should use dynamic memory allocation. MyClass * arrayOfPointers = new MyClass[3];. Don't forget to delete it, after using. delete[] arrayOfPointers;

Allocating an array of a class c++

How would I go about allocating an array of a class without constructing the class, so I could fill up the array later?
I was originally trying to use
Myclass * array = new Myclass[N];
But it tries to construct Myclass to N.
First just declare it without allocating
Myclass * array[N];
when you need it
for(int i=0;i<N;i++){
array[i] = new Myclass(/*params*/);
}
But consider using std::vector/std::list if you must not have to manage memory yourself.
If you really want to do that, (not sure why), you could try
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{ cout << "helo" << endl; }
};
int main(int argc, char *argv[])
{
int size = 4;
// Here is the trick, pointer to pointer.
MyClass **vec = new MyClass *[size];
cout << "before" << endl;
for (int i = 0; i < 4; ++i)
vec[i] = new MyClass;
// remember to free the vec
return 0;
}
Someone suggested placement new, so here it goes:
// allocate space
std::vector<unsigned char> mybuffer(N * sizeof(Myclass));
Myclass *array = reinterpret_cast<Myclass *>(&mybuffer[0]);
// when you're ready to use it
new( &array[0] ) Myclass(2);
new( &array[1] ) Myclass(3);
// etc...
// when you're done with it
array[0].~Myclass();
array[1].~Myclass();
// etc....
Of course, it is undefined behaviour to use array[x] before you have new'd it, or after you called the destructor.
This is generally something you wouldn't use as a solution to a "normal" problem. Consider actually defining a default constructor that does nothing, and having a function you call later which enhances the objects above their default state.
If you can use C++11, the optimal solution for you is probably std::vector<MyClass> with emplace-base insertions:
class MyClass {
public:
MyClass(int a, bool b, char c); // some non-default constructor
MyClass(double d); // another constructor
void bar();
};
void foo(int n) {
std::vector<MyClass> mv;
mv.reserve(n); // not even needed but beneficial if you know the final size.
// emplace_back uses perfect forwarding to call any arbitrary constructor:
mv.emplace_back(2, false, 'a');
mv.emplace_back(3, true, 'b');
mv.emplace_back(3.1415926535);
// can iterate vector easily:
for (auto &i : mv) {
i.bar();
}
// everything destructed automatically when the collection falls of scope ...
}
This creates the values in the collection directly without a copy and defers any construction of elements until you are ready, unlike new[], which makes a bunch of default objects at array-creation time. It is generally better than placement new as well, since it doesn't leave open opportunities for missed destruction or destructing an invalid memory location as well as being just easier to read.
Alternatively, you may use boost::optional.
So in your case:
std::vector<boost::optional<Myclass>> array(N);

Problem passing a list of objects to another class, C++

Below I have written a sample program that I have written to learn about passing a list of objects to another class. I talk about the problems I am having below.
#include <iostream>
#include <vector>
using namespace std;
class Integer_Class
{
int var;
public:
Integer_Class(const int& varin) : var(varin) {}
int get_var() { return var; }
};
class Contains_List
{
typedef Integer_Class* Integer_Class_Star;
Integer_Class_Star list;
public:
Contains_List(const Integer_Class_Star& listin) : list(listin) {}
Integer_Class* get_list() { return list; }
};
int main (int argc, char * const argv[])
{
// Create a vector to contain a list of integers.
vector<Integer_Class> list;
for(int i = 0; i < 10; i++)
{
Integer_Class temp_int(i);
list.push_back(temp_int);
}
This is where the errors start occuring. Could someone please look at the second class definition and the code below and shed some light on what I'm doing wrong. Thank you so much, as always!
// Import this list as an object into another object.
Contains_List final(list);
// Output the elements of the list by accessing it through the secondary object.
for(int i = 0; i < 10; i++)
{
cout << final.get_list()[i].get_var();
}
return 0;
}
You don't mention what sort of errors you are getting, but one very obvious problem with your code is that the constructor for Contains_List expects a pointer to Integer_Class while the parameter you are sending it (list) is of type vector<Integer_Class>.
A vector is not the same as an array, so you cannot pass it as pointer to the type it contains. Either change your constructor to accept a vector or pointer/reference to vector, or change the code that is causing you problems so that it sends it a pointer to an array.
The 'Contains_List' constructor takes in an 'Integer_Class*'
You declare 'list' to be of type 'vector', yet you pass it to the the 'Contians_List' constructor. You should change the 'Contains_List' class so that it holds a vector instead of an Integer_List array. The two are not interchangeable.
You could also change the vector to be an array of Integer_List's instead, if you so wished.