5

I have a class "Employee" I want to create a array of pointers for that.

Will this work out?

Employee *employeeArr[size];

In my "for loop" something like this

employeeArr[i] = new Employee(surname , firstname , gender); // constructor implemented Employee( para1, para2, para3)

OR should I write

Employee *employeeArr = new Employee[size];

And fill everything with "dots" like

employeeArr[i].setSurname(surname);

Can you explain the reason as well, I'm really new to pointers. The second one was told to me by someone else but I couldn't get an answer as to why I can't use the first one. Also if possible, do not mention std::array or std::vector, I'm still too new

Dumbass
  • 69
  • 4
  • 7
    You should rather use a `std::vector> employeeArr(size);`. So no, neither of your approaches is _the correct way_. –  Feb 11 '18 at 19:38
  • 3
    Welcome to [so], please take the [tour] and visit the [help]. This is not a tutoring site. You should read up on [ask] well received questions. – StoryTeller - Unslander Monica Feb 11 '18 at 19:39
  • If you are too new for vectors don't mess with pointers in the first place. – BessieTheCookie Feb 11 '18 at 19:41
  • 2
    StackOverflow is for specific programming questions, not general tutorials. Some search terms that might lead you to resources relating to what you need to read about are "dynamic memory allocation", "STL containers" "std::vector" and "smart pointers". Be aware that pointers are often not needed, a relevant question to this is [here](https://stackoverflow.com/questions/22146094/why-should-i-use-a-pointer-rather-than-the-object-itself). Sadly we can't "not mention" array or vector because they are more ingrained to the language than you think. – patatahooligan Feb 11 '18 at 19:49
  • @patatahooligan Depends on lazycalyptus :-P ... –  Feb 11 '18 at 21:09
  • If `size` is not a constant expression, then `Employee *employeeArr[size];` is ill-formed (though e.g. GCC will accept it as a language extension if `employeeArr` is an automatic variable). This is one more reason to use `vector` instead. – Arne Vogel Feb 11 '18 at 23:33

5 Answers5

4

Sorry to surprise you:
None of the examples you give should be considered as as the "correct way" to handle collections of classes in c++.

Also if possible, do not mention std::array or std::vector, I'm still too new

No, that's the wrong path hauling up the mare. The proper usage of raw pointers and raw arrays is certainly beyond your capabilities, if you can't grasp how to deal with std::array or std::vector primarily.


Supposed your Employee class looks like

struct Employee {
    std::string surname_;
    std::string firstname_;
    enum Gender {
       Female = 'F' ,
       Male = 'M' ,
       Unxpecified = 'X'
    } gender_;
};

and you have an overload for the std::operator>>()

std::istream& operator>>(std::istream& is, Employee& employee) {
    char genderSymbol;
    is >> employee.surname_ >> employee.firstname_ >> genderSymbol;
    switch(genderSymbol) {
    case 'F':
    case 'M':
    case 'X':
         employee.gender_ = (Employee::Gender)genderSymbol;
         break;
    default:
        is.setstate(std::ios_base::failbit);
        break;
    }
}

One good and idiomatic way to represent that Employee array would be to use a

std::vector<Employee> employeeArr;

and fill it in a loop:

Employee employee;
while(std::cin >> employee) {
    employeeArr.emplace_back(employee);
}

If you really need pointers (references) you may consider to use smart pointers as provided with the Dynamic Memory Management utility classes.

For instance you may decide to have a

std::vector<std::unique_ptr<Employee>> employeeArr;

and initialize it like

while(std::cin >> surname >> firstname >> gender) {
    employeeArr.emplace_back(std::make_unique<Employee>(surname , firstname , gender));
}

This comes into consideration if you want to manage pools of hierarchically organized class instances like:

struct Employee {
    virtual ~Employee() {}
    std::string surname_;
    std::string firstname_;
    enum Gender {
       Female = 'F' ,
       Male = 'M' ,
       Unxpecified = 'X'
    } gender_;
};

struct IForeman : Employee {
    virtual std::vector<const Employee const*> TeamMembers() const = 0;
    virtual void AddTeamMember(const Employee const* member) = 0;
};

class Foreman : public IForeman {
     str::vector<const Employee const*> teamMembers_;
public:
     std::vector<const Employee const*> TeamMembers() const {
         return teamMembers_;
     }
     void AddTeamMember(const Employee const* member) {
         teamMembers_.push_back(member);
     }
};

Consider to hand out owned or shared pointers to related connections using plain const pointers.

  • @user9335240 No need for ALL CAPS! I'm ole but not blind. I mentioned the _no copy_ version as well, so what? –  Feb 11 '18 at 20:00
  • Sorry, In the first solution (vector) a copy of the employee object in the vector. It is sometimes better in threading applications, but it has a performance penalty in C++ (object creation is expensive in C++, unlike Java). You must trade-off memory and speed for thread consistency. And the choice is yours. But the second solution (vector >) is good, performant, but may be sometimes unsafe, C++ needs a lot of thinking, all those solutions are good, but every problem has its optimal solution. – user9335240 Feb 11 '18 at 20:07
  • 1
    @user9335240 Well, you can have good hopes that the 1st solution provides well optimized copying behavior with any decent modern c++ compiler. –  Feb 11 '18 at 20:10
2

Also if possible, do not mention std::array or std::vector, I'm still too new

You got it backwards. If you are too new, then you should be using std::array and std::vector. Do not use built-in arrays and do not do manual memory management if you're new.

What you should be using, is:

#include <array>
// ...
std::array<Employee, size> employeeArr;

if size is known at compile-time and will never change. If it's not known at compile-time, or if the array needs to grow dynamically, then use vector:

#include <vector>
// ...
std::vector<Employee> employeeArr;

and then add Employee objects to it using push_back():

employeeArr.push_back(Employee(/* ... */));

And there's no pointers involved here either. Just values.

Once you get more familiar with containers, then you can delve deeper into C++ and learn about pointers and memory management.

Nikos C.
  • 47,168
  • 8
  • 58
  • 90
1

The first example creates an array of pointers to Employee objects, while the second one creates a dynamically allocated array of Employees. These are completely different things.

If you don't understand vectors, you should not be messing with pointers because they are easy to misuse. I would recommend learning C++ step by step with a good book if you don't already have one.

As far as I know, in most situations, the size of a static array must be a constant expression (able to be computed at compile time). That's why your first example does not work. If you want arrays with an unknown size then you should use vectors or dynamic arrays.

Once you understand vectors you will realize that they are much more convenient than dynamic arrays since they handle things like freeing memory automatically for you. Another nice thing about vectors is that you can resize them after they are created.

BessieTheCookie
  • 4,571
  • 4
  • 13
  • 32
0

C++ is a very flexible language, and the decision is completely yours.

  1. You can make an array of pointers (well, if you did so, memory handling will be completely manual, and error-prone).
  2. You can make a vector of pointers (vector is better than array in that it is flexible in adding and removing without a lot of manual code writing)
  3. You can use "smart-pointers", which means array (or vector as a better practice, (and maybe a smart-pointer of a vector)) of smart-pointers, this smart-pointers handle the memory for you.

For example:

vector<shared_ptr<Employee> > employees;
employees.push_back(make_shared<Employee>(surname, firstname, gender));

make_shared makes a (shared pointer) of the newly created employee. Shared pointer is an automatically reference-counted pointer, which is great in threading and in sharing object (pointer to object) between different objects.

shared_ptr in CPP Reference

unique_ptr in CPP Reference

You can see this question for an example of using unique_ptr

user9335240
  • 1,629
  • 1
  • 5
  • 13
0

Employee *employeeArr[size]; is an array of Employee pointers so the size is static which is known at compile-time. Whereas Employee *employeeArr = new Employee[size]; is a pointer to a dynamic array of Employee objects; not pointers.

Make difference and use each cautiously.

You can use class vector where you don't matter of allocatio-de-allocation of dynamic memory.

Here is an example showing the 3 possible usages:

#include <iostream>
#include <vector>
#include <string>


class Employee{
    public:
        Employee(){} // Imporatant for a dynamic array of objects
        Employee(const std::string, const std::string, const bool);

        void set(const std::string, const std::string, const bool);
        // some other methods here
        void print()const;

    private:
        std::string surName, firstName;
        bool  gender;
};

Employee::Employee(const std::string sur, const std::string first, const bool gend) : 
    surName(sur),
    firstName(first),
    gender(gend){

}

void Employee::set(const std::string sur, const std::string first, const bool gend){
    surName = sur;
    firstName = first;
    gender = gend;
}

void Employee::print()const{
    std::cout << "surName: " << surName << std::endl;
    std::cout << "firsName: " << firstName << std::endl;
    gender ? std::cout << "Male" : std::cout << "Female" ;
    std::cout << std::endl;
}


int main(){

    // 1: An array of pointers:
    Employee* empl[3];
    std::string surName, firstName;
    bool gender;

    for(auto i(0); i != 3; ++i){
        std::cout << "surName: ";
        std::cin >> surName;
        std::cout << "firstName: ";
        std::cin >> firstName;
        std::cout << "gender: ";
        std::cin >> gender;
        empl[i] = new Employee(surName, firstName, gender);
    }

    for(auto i(0); i != 3; ++i)
        empl[i]->print();

    std::cout << "_________________________" << std::endl;

    // 2: A pointer to a dynamic array:

    Employee* empl2 = new Employee[3]; // default constructor is imortant here
    for(auto i(0); i != 3; i++){
        std::cout << "surName: ";
        std::cin >> surName;
        std::cout << "firstName: ";
        std::cin >> firstName;
        std::cout << "gender: ";
        std::cin >> gender;
        empl2[i].set(surName, firstName, gender);
    }

    for(auto i(0); i != 3; ++i)
        empl2[i].print();

    delete[] empl2;

    std::cout << "_________________________" << std::endl;

    // 3: with vectors:

    std::vector<Employee> vecEmpl; // default ctor is not important here

    for(auto i(0); i != 3; ++i){
        std::cout << "surName: ";
        std::cin >> surName;
        std::cout << "firstName: ";
        std::cin >> firstName;
        std::cout << "gender: ";
        std::cin >> gender;

        Employee emp(surName, firstName, gender);
        vecEmpl.push_back(emp);
    }

    for(auto i(0); i != 3; ++i)
        vecEmpl[i].print();


    std::cout << std::endl << std::endl;
    return 0;
}
Raindrop7
  • 3,805
  • 3
  • 14
  • 27