How to allocate a struct and then increase its size? - c++

I have a struct data
struct dataStruct{
int mID;
float mX;
};
a fillData method
void fillData(data *pt)
{
pt = new data(10);
}
and the main function
int main(int argc, char** argv)
{
dataStruct *myData;
fillData(myData);
myData[4].mID = 10;
std::cout << myData[4].mID << " " << myData[5].mID << std::endl;
}
and a couple of questions:
how can allocate myData inside the fillData method so that the allocation is not local to fillData?
Can the size of myData be increased by later calls to fillData and thus changing the size from i.e. 10 to 100 ?
Would it be better to manage the dataStruct pointer using std::unique_ptr ?

I am guessing at what you want, as it is not clear.
Here is a much better way to handle a collection of objects whose size needs to change dynamically:
struct dataStruct
{
dataStruct() : mID(0), mX(0.0f) {} // default to value 0
dataStruct(int mID, float mX) : mID(mID), mX(mX) {}
int mID;
float mX;
};
int main(int argc, char** argv)
{
std::vector<dataStruct> myData(10); // create 10 objects with values 0
myData[4].mID = 10;
std::cout << myData[4].mID << " " << myData[5].mID << std::endl;
// now resize to 100 objects, with 0 values for any new objects
myData.resize(100);
std::cout << myData[4].mID << " " << myData[99].mID << std::endl;
}
Live example:
http://ideone.com/5nyPOp
No memory leaks! No uninitialized data (which is used in your original code). And good debug compilers will bounds check for you.

For the first question, fillData() needs to accept either a reference
void fillData(data *&pt) // makes change of pt visible to the caller
{
pt = new data(10);
}
int main()
{
data *pointer;
fillData(pointer);
}
or a pointer to a pointer
void fillData(data **pt) // makes change of *pt visible to the caller
{
*pt = new data(10);
}
int main()
{
data *pointer;
fillData(&pointer); // pass the address of pointer
}
For the second question, somehow fillData() needs to be told how many objects to allocate, and be able to release.
void fillData(data *&pt, int size)
{
delete [] pt;
pt = new data[size];
}
int main()
{
data *pointer = NULL; // otherwise first call of fillData() will fail on the delete statement
fillData(10);
// use pointer like an array of 10 elements
fillData(20); // note previous contents of pointer are lost
// use pointer like an array of 10 elements
}
Answer to your third question: Short answer: No.
Longer answer to your third question: If you want a resizable set of your data, use a standard container (like std::vector<data>) instead. One advantage of this is that standard containers can be resized cleanly (e.g. if resizing from 10 to 20 elements, the first ten elements are retained).
Note: it is possible to resize things allocated directly with operator new (e.g. with additional book-keeping to keep track of current and new sizes) but I have not done it in the examples above - because using a standard container is a vastly better approach in practice.

Related

reference vs smart_ptr for dependency inversion

I have two data types, A and B and I have to store the A instance in B in order to refer the A instance in a B method. Both are instantiated in the main function, so the two variables would live for as long as the program is running.
I don't want to make copies, I want to use something like an old fashioned pointer but also I want to follow (and learn) the modern C++ best practices. So what is the most appropriate type to store the A instance in B? (and why?)
I thought that if I want to use a pointer, the best way with modern C++, is using smart pointers, but references seems easier and lighter, so what is the best practice to refer to a variable created in a scope (for example in the main function) and used in another scope (for example a method of a class that has a pointer to that variable), knowing that the scope where that variable is created lives for as long as the class exists (when the stack is deallocated, it will free both the variable and the object that has the variable reference)?
struct A {};
struct B {
A & a;
B(A & a) : a(a) {}
};
int main() {
A a{};
B b{a};
}
or
#include<memory>
struct A {};
struct B {
std::shared_ptr<A> a;
B(auto a) : a(a) {}
};
int main() {
B b{std::make_shared<A>()};
}
The program I'm making is essentially a bunch of test to learn SDL2, I published the repo here https://github.com/antcolag/prova-sdl, B is the App class and A is the EventHandler class instantiated inside the main function.
As #πάντα-ῥεῖ noticed in my particular case the only reasonable way is with smart pointer, because I'm trying to use std::thread and std::atomic, but in a most general case what is the best way to replace the old C style pointers, with a more modern approach, when a variable is allocated in the stack, used by some other object and then deallocated from the stack together with the object?
If i understood the problem, you want to move the instance to a different "owner", with A a; it is posible but it requires a memcpy() if the scope of the instance is deleted. The most easy solution is to contain it in a shared scope, that is bad because it can be a global scope, the next best thing is to pass the reference to the owner (the structure that contains the data). At the end this are cheap solutions if they are applied over and over, modern c++ has a lot of tools for memory control / flow; most of those are pointer based because the data pointer copy is trivial, note that only in combination with std::atomic or a similar lib is suitable for multithreading.
This example shows how a data pointer can be moved and used without any fancy c++, a small note on the pointer idea, in the example the pointer address is not changing as long as it is not deleted, any reference made will persist even if ref_objs order is changed, the data is "on the wild" and the pointer is a number.
#include <iostream>
struct Object {
int num = 69;
};
struct Container {
// Better to use std::vector but
// this shows better what it does
// Olso can be replaced with
// Object * ref_objs [n] if n is fixt
Object ** ref_objs;
uint32_t n_obj;
uint32_t n_obj_max;
void provision_for(uint32_t res_len){
// To initialize data is better to use
// use a method insted of the constructor;
// This alocates n spaces of obj pointers
ref_objs = new Object * [res_len];
n_obj_max = res_len;
n_obj = 0;
}
void clear_all(){
uint32_t i;
for (i=0; i < n_obj; i++){
delete ref_objs[i];
}
delete [] ref_objs;
n_obj = 0;
}
Object * add_obj(){
Object * ret = nullptr;
if (n_obj < n_obj_max){
ref_objs[n_obj] = new Object;
ret = ref_objs[n_obj];
n_obj++;
}
return ret;
}
void del_obj(uint32_t n){
if (n < n_obj - 1){
// keeps them alighned
ref_objs[n] = ref_objs[n_obj];
}
delete ref_objs[n_obj];
n_obj--;
}
int recive_obj(Object * ref){
int res = 1;
if (n_obj < n_obj_max){
ref_objs[n_obj] = ref;
n_obj++;
res = 0;
}
return res;
}
int transfer_to(Container * to, uint32_t item){
int res = 1;
if (to->recive_obj(ref_objs[item]) == 0){
if (item < n_obj - 1){
ref_objs[item] = ref_objs[n_obj - 1];
} else {
ref_objs[item] = nullptr;
}
n_obj --;
res = 0;
}
return res;
}
Object * at (uint32_t at){
return ref_objs[at];
}
Object & operator [](uint32_t at){
// [0] is added to asure the compiler that it
// is a instance and not an array
return ref_objs[at][0];
}
};
int main(void){
Container container_a;
Container container_b;
container_a.provision_for(10);
container_b.provision_for(15);
Object * x = container_a.add_obj();
Object * y = container_a.add_obj();
Object * z = container_b.add_obj();
std::cout << "container_a len -> " << container_a.n_obj << std::endl;
std::cout << "container_b len -> " << container_b.n_obj << std::endl;
y->num = 200;
container_a.transfer_to(&container_b, 0);
container_b[1].num = 400;
std::cout << "container_a obj[0].num -> " << container_a[0].num << std::endl;
std::cout << "container_b obj[0].num -> " << container_b[0].num << std::endl;
std::cout << "container_b obj[1].num -> " << container_b.ref_objs[1]->num << std::endl;
container_a.del_obj(0);
container_a.clear_all();
container_b.clear_all();
return 0;
}
(This example is template suitable, just change all Object with the typename and the instance will be Container<Object> container_a;)

Difficulty understanding c++ polymorphism

What I am trying to achieve is creating a superclass array of subclass objects.
In this particular test I'm working on, I want to have an animal array that has some dog objs and some cat objs while they maintain their attributes.
#include <iostream>
using namespace std;
//ANIMAL
class animal
{
protected:
int ID;
string name;
public:
animal(string = "Unknown");
int get_ID() { return ID; }
virtual string get_name() { return name; }
};
animal::animal(string n) { name = n; }
//DOG
class dog : public animal
{
static int newID;
string sound;
public:
dog(string = "Corgi", string = "Woof!");
string get_name() { return sound + " " + name; }
};
int dog::newID = 0;
dog::dog(string n, string s) : animal(n)
{
newID++;
ID = newID;
cout << ID << "\t";
sound = s;
}
//CAT
class cat : public animal
{
static int meowID;
string color;
public:
cat(string = "Munchkin", string = "Calico");
string get_name() { return color + " " + name; }
};
int cat::meowID = 89;
cat::cat(string n, string c) : animal(n)
{
meowID++;
ID = meowID;
cout << ID << "\t";
color = c;
}
//MAIN
int main(int argc, char* argv[])
{
animal** test;
animal* p;
for (int i = 0; i < 6; i++)
{
p = new dog;
p++;
}
cout << "\n";
for (int i = 0; i < 6; i++)
{
p = new cat;
p++;
}
cout << "\n";
test = &p;
cout << (*test-7)->get_ID();
return 0;
}
What I've learned so far is that p isn't an array, and it keeps pointing to different memory addresses through the loops.
I cannot do animal** test = new dog[6]; as it is an invalid initialization. Even if that worked I would have trouble cascading another array segment of cat.
This is the output I obtained:
1 2 3 4 5 6
90 91 92 93 94 95
0
The first line is displaying dog IDs being invoked 6 times, and the second line is displaying cat IDs being invoked 6 times. (*test-7)->get_ID();is the last number.
It seems the constructors are being invoked right. However, I have no idea where my pointer is pointing, since I am expecting 91 not 0.
How do I get an animal array that I can access information from each element? For example,
animal** myArray;
{do something}
cout << myArray[2].get_name() << endl << myArray[7].get_ID();
and it outputs
Woof! Corgi
91
One important detail about the animal class: polymorphic types can run into issues when their destructors are called but those destructors are not virtual. It is recommended that you make the destructor of the base class virtual, even if that class itself does not actually need a destructor. In this case, you can tell the compiler that you want the destructor to be virtual but generate a default implementation of it with:
virtual ~animal() = default;
Add the above line in the public: section of your animal class. This ensures that any derived classes that you define later on will get a virtual destructor automatically.
Now to the rest of your code:
p = new dog;
So far, so good. But then this:
p++;
does nothing useful other than making the pointer point to an invalid address. Then in the next iteration, another p = new dog; will be performed. The previous dog object you allocated is now lost forever. You got a so-called "leak".
It seems you expect new to allocate objects an a way that lays them out in memory one after another. That is not the case. new will allocate memory in an unpredictable location. As a result, this:
*test-7
cannot possibly work, as the objects are not laid out in memory the way you expected. What you get instead is an address to some memory location 7 "positions" before the latest allocated object that pretty much certainly does not point to the animal object you were hoping. And when you later dereference that you get undefined behavior. Once that happens, you cannot reason about the results anymore. They can be anything, from seeing wrong text being printed to your program crashing.
If you want an array of animal pointers, you should specifically create one:
animal* animals[12];
This creates an array named animals that contains 12 animal pointers. You can then initialize those pointers:
for (int i = 0; i < 6; i++) {
animals[i] = new dog;
}
cout << "\n";
for (int i = 6; i < 12; i++) {
animals[i] = new cat;
}
You then just specify the array index of the one you want to access:
cout << animals[0]->get_ID() << '\n'; // first animal
cout << animals[6]->get_ID() << '\n'; // seventh animal
Don't forget to delete the objects after you're done with the array. Since animals is an array, you can use a ranged for loop to delete all objects in it:
for (auto* animal_obj : animals) {
delete animal_obj;
}
However, all this low-level code is quite tedious and error-prone. It's recommended to instead use library facilities that do the allocations and cleanup for you, like std::unique_ptr in this case. As a first step, you can replace your raw animal* pointer with an std::unique_ptr<animal>:
unique_ptr<animal> animals[12];
(Don't forget to #include <memory> in your source file, since std::unique_ptr is provided by that library header.)
Now you've got an array of smart pointers instead of raw pointers. You can initialize that array with:
for (int i = 0; i < 6; i++) {
animals[i] = make_unique<dog>();
}
cout << "\n";
for (int i = 6; i < 12; i++) {
animals[i] = make_unique<cat>();
}
Now you don't need to delete anything. The smart pointer will do that automatically for you once it goes out of scope (which in this case means once the animals array goes out of scope, which happens when your main() function exits.)
As a second step, you can replace the animals array with an std::vector or an std::array. Which one you choose depends on whether or not you want your array to be able to grow or shrink later on. If you only ever need exactly 12 objects in the array, then std::array will do:
array<unique_ptr<animal>, 12> animals;
(You need to #include <array>.)
Nothing else changes. The for loops stay the same.
std::array is a better choice than a plain array (also known as "built-in array") because it provides a .size() member function that tells you the amount of elements the array can hold. So you don't have to keep track of the number 12 manually. Also, an std::array will not decay to a pointer, like a plain array will do, when you pass it to functions that take an animal* as a parameter. This prevents some common coding bugs. If you wanted to actually get an animal* pointer from an std::array, you can use its .data() member function, which returns a pointer to the first element of the array.
If you want the array to be able to grow or shrink at runtime, rather than have a fixed size that is set at compile time, then you can use an std::vector instead:
vector<unique_ptr<animal>> animals;
(You need to #include <vector>.)
This creates an empty vector that can store elements of type unique_ptr<animal>. To actually add elements to it, you use the .push_back() function of std::vector:
// Add 6 dogs.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<dog>());
}
// Add 6 cats.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<cat>());
}
Instead of push_back() you can use emplace_back() as an optimization, but in this case it doesn't matter much. They key point to keep in mind here is that a vector will automatically grow once you push elements into it. It will do this automatically without you having to manually allocate new elements. This makes writing code easier and less error-prone.
Once the vector goes out of scope (here, when main() returns,) the vector will automatically delete the memory it has allocated to store the elements, and since those elements are smart pointers, they in turn will automatically delete the animal objects they point to.
If you're new to C++, it's important that you get started on the right foot and to follow modern best practices, namely:
Avoid using pointers, new, delete, new[] and delete[].
Instead use smart-pointers (unique_ptr, shared_ptr, but don't use auto_ptr!).
Use the make_ functions instead of new. That way you don't need to worry about delete.
See Advantages of using std::make_unique over new operator
Use std::vector<T> (and std::array<T,N> if you have fixed-size collections) instead of new[] or p** (and never use malloc or calloc directly in C++!)
I note that you should also generally prefer Composition over Inheritance, but with trivial examples like yours it's difficult to demonstrate the concept because a Dog and a Cat "are" Animals.
I also note that when the possible set of subclasses is known at compile-time you should consider using a union-type instead of subclassing because it allows consumers to exhaustively work with returned values without needing to use RTTI or guesswork.
This can be done with using AnAnimal = std::variant<cat,dog>.
Anyway, this is what I came-up with. The class animal, class dog, and class cat code is identical to your posted code (and is located within the // #region comments), but the #include and using statements at the top are different, as is the main method.
Note that my code assumes you have a compiler that complies to the C++14 language spec and STL. Your compiler may default to C++11 or older. The std::make_unique and std::move functions require C++14.
Like so:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// Containers:
using std::vector;
using std::string;
// Smart pointers:
using std::unique_ptr;
using std::move;
using std::make_unique;
// IO:
using std::cout;
using std::endl;
// #region Original classes
//ANIMAL
class animal
{
protected:
int ID;
string name;
public:
animal(string = "Unknown");
int get_ID() { return ID; }
virtual string get_name() { return name; }
};
animal::animal(string n) { name = n; }
//DOG
class dog : public animal
{
static int newID;
string sound;
public:
dog(string = "Corgi", string = "Woof!");
string get_name() { return sound + " " + name; }
};
int dog::newID = 0;
dog::dog(string n, string s) : animal(n)
{
newID++;
ID = newID;
cout << ID << "\t";
sound = s;
}
//CAT
class cat : public animal
{
static int meowID;
string color;
public:
cat(string = "Munchkin", string = "Calico");
string get_name() { return color + " " + name; }
};
int cat::meowID = 89;
cat::cat(string n, string c) : animal(n)
{
meowID++;
ID = meowID;
cout << ID << "\t";
color = c;
}
// #endregion
int main()
{
// See https://stackoverflow.com/questions/44434706/unique-pointer-to-vector-and-polymorphism
vector<unique_ptr<animal>> menagerie;
// Add 6 dogs:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<dog>() );
}
// Add 6 cats:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<cat>() );
}
// Dump:
for ( auto &animal : menagerie ) {
cout << "Id: " << animal->get_ID() << ", Name: \"" << animal->get_name() << "\"" << endl;
}
return 0;
}

Is calling std::fill() before std::erase() produces undefined behaviour?

I'm working on a C++11 program, where security is important and my task is to set to 0 the used memory after erasing it.
I have an std::map mapping from int to an std::vector of pointer to class. I have the index in std::map and a pointer to the instance I would like to delete.
The following code produces the output I want, however, I'm not sure if it's good formed code (or I would say I'm not sure if this code is ok or not).
I have 2 questions.
If the following code is ok,
It can only be compiled with -fpermissive, I don't understand the compiler error message.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
class MyClass
{
private:
int num;
public:
MyClass(int num) { this->num = num; }
int GetNum() const { return this->num; }
};
void PrintWorkingData(const std::map<int, std::vector<MyClass*>>& working_data, int idx)
{
std::cout << "working_data[" << idx << "] is an std::vector, size: " << working_data[idx].size() << ", containing the following items: " << std::endl;
for (std::vector<MyClass*>::const_iterator it = working_data[idx].begin(); it != working_data[idx].end(); it++)
{
std::cout << "(*it)->GetNum() = " << (*it)->GetNum() << std::endl;
}
}
int main()
{
MyClass* DeleteMyClass;
std::map<int, std::vector<MyClass*>> working_data;
working_data[0].push_back(new MyClass{4});
working_data[0].push_back(new MyClass{7});
working_data[1].push_back(new MyClass{11});
// the origonal code isn't like this; let's suppose
// we stored in the DeleteMyClass pointer the MyClass pointer
// that we would like to delete
working_data[1].push_back(DeleteMyClass = new MyClass{22});
working_data[1].push_back(new MyClass{33});
working_data[2].push_back(new MyClass{1000});
PrintWorkingData(working_data, 0);
PrintWorkingData(working_data, 1);
PrintWorkingData(working_data, 2);
PrintWorkingData(working_data, 3);
// so our task is to delete DeleteMyClass object from working_data[DeleteItemIndex]
// and fill with 0 where it was stored
int DeleteItemIndex = 1;
std::vector<MyClass*>::iterator pos = std::find(working_data[DeleteItemIndex].begin(), working_data[DeleteItemIndex].end(), DeleteMyClass);
if (pos == working_data[DeleteItemIndex].end())
{
std::cout << "Error: The item does not present in the working_data" << std::endl;
}
else
{
std::fill(pos, pos + 1, 0);
working_data[DeleteItemIndex].erase(pos);
delete DeleteMyClass;
std::cout << "The object successfully deleted" << std::endl;
}
PrintWorkingData(working_data, 0);
PrintWorkingData(working_data, 1);
PrintWorkingData(working_data, 2);
PrintWorkingData(working_data, 3);
return 0;
}
Setting a pointer value to nullptr doesn't change the data it points to. Erasing an element from a vector will overwrite that element with all the later ones in the vector, leaving (in this case) a second pointer in allocated memory (beyond the size of the vector) to the last element in the vector.
To erase the memory occupied by the object that DeleteMyClass points to, you'll have to handle the object destruction and memory freeing separately. This is not necessarily easy or straightforward, as there can be nuances (exception handling, array vs. non-array forms) that need to be addressed. You should also keep in mind that it is possible to inspect the memory of a running process, and view the data you're trying to erase while the object that uses it is live.
Here are several approaches that might work for you case.
One way to do this is to manually call the destructor, clear out the memory, then free it.
DeleteMyClass->~MyClass();
memset(DeleteMyClass, 0, sizeof(*DeleteMyClass));
delete (void *) DeleteMyClass;
The cast on the delete call is necessary to avoid calling the destructor, and the number of bytes to clear uses the type of DeleteMyClass, which will be incorrect if what is pointed to is a class derived from MyClass.
Another alternative is to use placement new with an already allocated memory buffer and a custom deallocator (after manually calling the destructor) to free up the memory.
A third possibility is to use custom new and delete functions, either for this specific class or globally.

Can a temporary object be passed by value without destroying the original in C++

In the following code, the pointer is reset when a struct S is destructed. I'd prefer a vector of structure values instead of pointers. Is there a way to add to the vector without the temporary getting destructed?
int* pi = nullptr;
struct S
{
S(int* i) { pi = i; }
~S() { pi = nullptr; }
};
int main(int argc, char* args[])
{
int i = 5;
std::vector<S> sVector;
sVector.push_back(S(&i));
std::cout << pi << std::endl; // outputs 0 instead of address
return 0;
}
You're looking for emplace_back:
sVector.emplace_back(&i);
That will construct an S in-place, no temporaries anywhere.
Note, however, that you have no guarantee that an append operation won't trigger a resize - and a resize would involve copying and destroying a bunch of Ss.

C++ error: unknown type name

So, here's my code:
/****************************************************************
File: Video.h
Description: class declarations
Author: David && Evan
Class: CSCI 120
Date: 2015 May 13
We hereby certify that this program is entirely our own work.
*****************************************************************/
#ifndef VIDEO_H
#define VIDEO_H
#include <iostream>
#include <string>
#include <vector>
#include "Person.h"
#include "Date.h"
using namespace std;
enum kind {MOVIE, TELEVISION, COMPUTER};
// 'MOVIE' = standalone film of any length, whether it's part of a franchise or not
// 'TELEVISION' = episode from mini- or recurring series
// 'COMPUTER' = online or locally hosted files
/* If need be, we can extend this by adding something for analog home movies,
i.e., camcorder tapes or 8mm film. */
namespace Vids
{
class Video{
public:
Video(); // default constructor
Video(string name, string audience, string location, vector<Person> directors,
vector<Person> actors, Date released);
virtual void display() = 0; // displays information for all objects of Video type
virtual void displayAll() = 0; // displays all information for one object
unsigned char getDirectorSize() const { return directorSize; }
unsigned char getActorSize() const { return actorSize; }
string getName() const { return name; }
string getAudience() const { return audience; }
string getLocation() const { return location; }
Date getReleased() const { return released; }
Date getViewed() const { return viewed; }
string Truncate(string str, size_t width) { // shortens output
if (str.length() > width)
return str.substr(0, width) + "...";
return str;
} // truncate
protected:
short runtimeMinutes;
/* Theoretically runtime could be unsigned, but we might eventually
need negatives for special values. I doubt we'll see one needing
more than 32K minutes, so no worry about overflow. */
unsigned char directorSize;
// number of elements in each vector, shouldn't need more than 255
unsigned char actorSize;
string name; // title of movie
string audience; // PG = "Plenty Guns", PG-13 = "13 or more guns"
string location;
/* Location is a catch-all field for: URL, shelf disc is on, format
type, name of person it is loaned to, etc. */
vector<Person> directors(directorSize);
/* David: I considered using other containers, but none of them
offered any obvious benefits over the vector. */
vector<Person> actors(actorSize);
Date released;
Date viewed;
/* 'viewed' can be used to answer the question: "What haven't i
watched lately?" */
}; // end class Video
} // end namespace Vids
#endif
And compiling [with several other files] gives me this:
$ g++ *.cpp -o Project3
In file included from Computer.cpp:12:
In file included from ./Computer.h:15:
./Video.h:68:29: error: unknown type name 'directorSize'
vector<Person> directors(directorSize);
^
./Video.h:71:26: error: unknown type name 'actorSize'
vector<Person> actors(actorSize);
^
directorSize is declared in the same scope as directors, so why is the compiler not recognizing it?
The line
vector<Person> directors(directorSize);
is not the right syntax for declaring a member variable.
Change it to
vector<Person> directors;
Similarly, change
vector<Person> actors(actorSize);
to
vector<Person> actors;
Given that you can get the number of items in a vector by calling the size() member function, you don't need the member variables:
unsigned char directorSize;
unsigned char actorSize;
Remove them.
Well, this comment is puzzling:
/* Theoretically runtime could be unsigned, but we might eventually
need negatives for special values. I doubt we'll see one needing
more than 32K minutes, so no worry about overflow. */
unsigned char directorSize;
unsigned char maxes out at 255 so if you need up to 32K then you will have to use a different type. In fact it would be better to remove this variable entirely, and retrieve the size by doing directors.size() as needed.
It is possible to initialize the vector in the class definition:
vector<Person> directors{directorSize};
However this would cause undefined behaviour if you hadn't initialized directorSize in the constructor initializer list (because you would be using an uninitialized variable directorSize).
It would be much better to change this to:
vector<Person> directors;
and presumably in your constructor initializer list, or constructor body, you will add some items into this vector.
I'll boil your problem down to a trivial case to demonstrate the problem, along with applying the "m_" member variable prefix popular among some engineers for highlighting a variable that is a "member" of something.
#include <vector>
class Class {
protected:
unsigned char m_directorSize;
std::vector<int> m_directors(m_directorSize);
};
int main()
{
Class x;
}
This does not compile http://ideone.com/VJck4Q and by isolating the problem, we learned a lot.
The line of code:
std::vector<int> m_directors(m_directorSize);
Look at the syntax of this
/typename/ /name/ ( /values/ );
The compiler thinks this is a member-function declaration, which is why it is expecting a type:
std::vector<int> something(unsigned char directorSize);
would declare a member-function called "something" that takes a parameter, directorSize, of type unsigned char. Incidentally: unsigned char is almost guaranteed to be an 8-bit value, capable of storing 0 through 255. That's a terrible choice for a size variable. There is a standard type, size_t for storing non-negative sizes and ssize_t for storing signed sizes.
It's unclear why you think you should be passing directorSize to the vector, but you can't declare a member with a function call.
If you want to shape the default behavior of an object at construction time of your class, you need to use a constructor (or use C++11/C++14, but we don't know if you're doing that):
class Foo_Sized {
std::vector<int> m_vec;
public:
Foo() : m_vec(250) // default construct 250 elements
{}
};
If you were using C++11/14:
class Foo_Sized {
std::vector<int> m_vec = std::vector<int>(250);
};
But if you want to affect one member based on the value of another, the only place you can do that is in a member function, so in this case that means the constructor.
If you're going to create a static array like that, you probably want to be using std::array instead of std::vector - the whole point of vector is that it can grow dynamically.
std::array<int, 250> m_arr;
This declares an array of integers which has a capacity of 250 and has a fixed size. It's faster than a vector but it is always 250 large. You would then have to track the "in-use" count of it and other management overhead yourself.
std::vector<int> vec;
std::cout << vec.size() << '\n'; // prints 0
vec.push(10); // add a value of 10 to the vector.
vec.push(20); // vec is now { 10, 20 }
std::cout << vec.size() << '\n'; // prints 2
vec.push(30); // vec is now { 10, 20, 30 }
std::cout << vec.size() << '\n'; // prints 3
std::cout << vec[0] << '\n'; // prints 10
std::cout << vec[3] << '\n'; // undefined behavior, there is no 3rd element
std::array<int, 3> arr;
std::cout << arr.size() << '\n'; // prints 3: fixed size.
arr[0] = 10; // can't push, fixed size.
arr[1] = 20;
std::cout << arr.size() << '\n'; // still 3, always will be.
arr[2] = 30;
std::cout << arr.size() << '\n'; // still 3, always will be.
std::cout << arr[0] << '\n'; // prints 10
std::cout << arr[3] << '\n'; // compile error: outside fixed size
If you were worrying about memory allocation, you could tell the vector to allocate memory upfront like this:
class Foo_Reserved {
std::vector<int> m_vec;
public:
Foo() : m_vec() // default construct empty
{
m_vec.reserve(250); // reserve memory for 250 elements
}
};
There are a host of other issues with your code, but they aren't directly related to your question so I'm not going to address/highlight them.
But in terms of dealing with your directorSize issues, you should consider providing an accessor that queries the vector or expose a const reference to the vector (this somewhat breaks encapsulation tho since it allows external callers to write code based on assumptions about your internal structure).
class Foo {
public:
using vec_t = std::vector<int>;
protected:
vec_t m_vec;
public:
Foo() : m_vec() // default construct empty
{
}
size_t vecSize() const { return m_vec.size(); }
// or, return a look-don't-touch reference to the vector
const vec_t& getVec() const { return m_vec; }
};
Foo f{}; // C++14 initializer
f.vecSize();
f.getVec().size();