Kreetchy
Kreetchy

Reputation: 800

Is this the correct way of writing an array of pointers?

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

Upvotes: 5

Views: 368

Answers (5)

user9212993
user9212993

Reputation:

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.

Upvotes: 4

Nikos C.
Nikos C.

Reputation: 51890

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.

Upvotes: 2

Raindrop7
Raindrop7

Reputation: 3911

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;
}

Upvotes: 0

eesiraed
eesiraed

Reputation: 4654

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.

Upvotes: 1

user9335240
user9335240

Reputation: 1799

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

Upvotes: 0

Related Questions