classes

So we are supposed to do this program down below but I keep getting errors and I feel like the InstructorProfile.h class is too simple. I dont know what to put for the Public part. And I also need guidance as to what to do with the Instructor class since it has an array of 3.



You will design several classes to record personal and academic information about instructors at a university.
The classes are: InstructorProfile, Person, Instructor, and Course. All class attributes must be private and accessed through public member functions.

1. You need to write appropriate constructor, accessor and mutator functions for each class. The attributes of each class are summarized below:

InstructorProfile class has the following private attributes:
Person personalInfo
Instructor instructorInfo

Person class contains personal information and has the following private attributes:
string firstName
string lastName
char gender (‘M’ for Male and ‘F’ for Female)
long ssn

Instructor class contains academic information. You may assume every instructor teaches 3 courses. Instructor class has the following private attributes:
long empoyeeID
string officeNum
Course courses[3]

Course class contains individual course information and has the following private attributes:
long courseNumber
string courseName
int numberOfCredits
2. After you design your classes, write a main program that makes and displays a directory of instructors.
You should read a data file that contains a set of information about instructors and store the data in a vector of InstructorProfile named instructorDirectory:
vector<InstructorProfile> instructorDirectory;
You should make up the data file with at least 3 records of instructors.

This is my Instructor.h file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>

using namespace std;

class Instructor
{

 private:

  long employeeID;
  string officeNum;
  Courses courses[3];

 public:

  Instructor();
  void setInstructor(long, string, Courses[3]);

}
#endif


This is what I have for my InstructorProfile.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef INSTRUCTOR_PROFILE
#define INSTRUCTOR_PROFILE
#include <iostream>
#include <string>
#include "Person.h"

using namespace std;

class InstructorProfile
{
 private:

  Person personalInfo;
  Instructor instructorInfo;


 public:
  InstructorProfile();


};
#endif


This is my Person.h file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef PERSON
#define PERSON
#include <iostream>
#include <string>
#include "InstructorProfile.h"

using namespace std;

class Person
{
 private:

  string firstName;
  string lastName;
  char gender;
  long ssn;

 public:

  Person();
  void set(string, string, char, long);
  string getfirstName() const;
  char getgender() const;
  long getssn () const;

}
#endif;




Hi, sesslit805. Have you already solved your exercise?

I’m adding my two pennies to your issue. Hope my code could give you some inspiration.
You didn’t tell us if you already have a text file from which read the data, so I followed my imagination :-)
Please, do not consider that part, i.e. writing and reading from file, but only the “classes” part – the other part was only for testing.

First time you’ll use the program, maybe you want to uncomment these rows in the main() function:
n. 26 --> createMockInstrProf();
It creates a file named examples.txt (where… depends where is created your executable).

So, from the second execution, is better to have that line commented out.

Rows from 164 to 171:
1
2
3
4
5
6
7
8
//cout << "prof.getPersonalInfo().getFirstName(): "
//     << prof.getPersonalInfo().getFirstName()
//     << "\nprof.getPersonalInfo().getLastName(): "
//     << prof.getPersonalInfo().getLastName()
//     << "\nprof.getPersonalInfo().getGender(): "
//     << prof.getPersonalInfo().getGender()
//     << "\nprof.getPersonalInfo().getSsn(): "
//     << prof.getPersonalInfo().getSsn() << endl; 


Rows from 219 to 229:
1
2
3
4
5
6
7
8
9
10
11
//cout << "prof.getInstructorInfo().getEmployeeID(): "
//     << prof.getInstructorInfo().getEmployeeID()
//     << "\nprof.getInstructorInfo().getOfficeNum(): "
//     << prof.getInstructorInfo().getOfficeNum()
//     << "\nprof.getInstructorInfo().getCourses(): ";
//const Course* displaycou = prof.getInstructorInfo().getCourses();
//for(int i{0}; i<3; i++)
//    cout << displaycou[i].getCourseNumber() << ' '
//         << displaycou[i].getCourseName() << ' '
//         << displaycou[i].getNumberOfCredits() << "; ";
//cout << endl << endl; 

They merely shows the values read from the file.

It will take me more than one post to paste all the code.

Person.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef PERSON_H
#define PERSON_H

#include <string>

class Person
{
public:
    Person();
    Person(std::string name, std::string surname, char mf, long s_s_n);

    std::string getFirstName() const {return firstName;}
    std::string getLastName() const {return lastName;}
    char getGender() const {return gender;}
    long getSsn() const {return ssn;}

    void setFirstName(const std::string value) {firstName = value;}
    void setLastName(const std::string value) {lastName = value;}
    void setSsn(long value) {ssn = value;}
    void setGender(char value) {gender = value;}

    friend std::ostream& operator<<(std::ostream& os, const Person& pers);
    friend std::istream& operator>>(std::istream& is, Person& pers);

private:
    std::string firstName;
    std::string lastName;
    char gender {'X'}; // ‘M’ for Male and ‘F’ for Female
    long ssn {0};
};

#endif // PERSON_H 


Person.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "Person.h"
#include <iostream>

Person::Person()
{}

Person::Person(std::string name, std::string surname, char mf, long s_s_n) :
    firstName{name}, lastName{surname}, gender{mf}, ssn{s_s_n}
{}

std::ostream& operator<<(std::ostream& os, const Person& pers)
{
    os << std::string("[Person]") << std::endl;
    os << "First name: " << pers.getFirstName() << std::endl;
    os << "Last name: " << pers.getLastName() << std::endl;
    os << "Gender: " << pers.getGender() << std::endl;
    os << "Ssn: " << pers.getSsn() << std::endl;

    return os;
}

std::istream& operator>>(std::istream& is, Person& pers)
{
    std::string temp;
    is >> temp; // skip "[Person]"
    is >> pers.firstName >> pers.lastName >> pers.gender >> pers.ssn;

    return is;
}


Instructor.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef INSTRUCTOR_H
#define INSTRUCTOR_H

#include <string>
#include "Course.h"

constexpr int MAXCOURSES = 3;

class Instructor
{
public:
    Instructor();
    Instructor(long id, const std::string name, Course* cses);

    long getEmployeeID() const {return employeeID;}
    std::string getOfficeNum() const {return officeNum;}
    // getCourses() must return a const value, otherwise
    // the array could be modify from outside the class.
    const Course* getCourses() const {return courses;}

    void setEmployeeID(long value) {employeeID = value;}
    void setOfficeNum(const std::string& value) {officeNum = value;}
    void setCourses(const Course* value);

    friend std::ostream& operator<<(std::ostream& os, const Instructor& instr);
    friend std::istream& operator>>(std::istream& is, Instructor& instr);

private:
    long employeeID {0};
    std::string officeNum;
    Course courses[MAXCOURSES];

    void copyCoursesFromOutside(const Course* cses);
};

#endif // INSTRUCTOR_H 


Instructor.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include "Instructor.h"

Instructor::Instructor()
{}

Instructor::Instructor(long id, const std::string name, Course* cses) :
    employeeID{id}, officeNum{name}
{
    copyCoursesFromOutside(cses);
}

void Instructor::setCourses(const Course* value)
{
    copyCoursesFromOutside(value);
}

void Instructor::copyCoursesFromOutside(const Course* cses)
{
    for(int i{0}; i<MAXCOURSES; i++)
        courses[i] = cses[i];
}

std::ostream& operator<<(std::ostream& os, const Instructor& instr)
{
    os << std::string("[Instructor]") << std::endl;
    os << "Employee ID: " << instr.employeeID << std::endl;
    os << "Office num: " << instr.officeNum << std::endl;
    os << "Courses: ";
    for(int i{0}; i<3; i++){
        os << "\n - "
           << instr.courses[i].getCourseNumber() << ' '
           << instr.courses[i].getCourseName() << ' '
           << instr.courses[i].getNumberOfCredits();
    }
    os << std::endl;

    return os;
}

std::istream& operator>>(std::istream& is, Instructor& instr)
{
    std::string temp;
    is >> temp; // skip "[Instructor}"
    is >> instr.employeeID >> instr.officeNum;
    for(int i{0}; i<3; i++) {
        is >> temp; // discard "-"
        long l;
        is >> l;
        instr.courses[i].setCourseNumber(l);
        is >> temp;
        instr.courses[i].setCourseName(temp);
        int j;
        is >> j;
        instr.courses[i].setNumberOfCredits(j);
    }

    return is;
}


Courses.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef COURSE_H
#define COURSE_H

#include <string>

class Course
{
public:
    Course();
    Course(long number, std::string name, int credits);

    long getCourseNumber() const {return courseNumber;}
    std::string getCourseName() const {return courseName;}
    int getNumberOfCredits() const {return numberOfCredits;}

    void setCourseNumber(long value) {courseNumber = value;}
    void setCourseName(const std::string value) {courseName = value;}
    void setNumberOfCredits(int value) {numberOfCredits = value;}

private:
    long courseNumber {0};
    std::string courseName;
    int numberOfCredits {0};
};

#endif // COURSE_H 


Course.cpp:
1
2
3
4
5
6
7
8
#include "Course.h"

Course::Course()
{}

Course::Course(long number, std::string name, int credits) :
    courseNumber{number}, courseName{name}, numberOfCredits{credits}
{}

Last edited on
InstructorProfile.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef INSTRUCTORPROFILE_H
#define INSTRUCTORPROFILE_H

#include "Person.h"
#include "Instructor.h"

class InstructorProfile
{
public:
    InstructorProfile();
    InstructorProfile(Person pers, Instructor instr);

    Person getPersonalInfo() const {return personalInfo;}
    Instructor getInstructorInfo() const {return instructorInfo;}

    void setPersonalInfo(Person&& value) {personalInfo = value;}
    void setPersonalInfo(std::string name, std::string surname,
                         char mf, long s_s_n);
    void setInstructorInfo(Instructor&& value) {instructorInfo = value;}
    void setInstructorInfo(long id, const std::string name, Course* cses);

private:
    Person personalInfo;
    Instructor instructorInfo;
};

#endif // INSTRUCTORPROFILE_H 


InstructorProfile.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "InstructorProfile.h"

InstructorProfile::InstructorProfile()
{}

InstructorProfile::InstructorProfile(Person pers,
                                     Instructor instr) :
    personalInfo{pers}, instructorInfo{instr}
{}

void InstructorProfile::setPersonalInfo(std::string name, std::string surname,
                                        char mf, long s_s_n)
{
    personalInfo.setFirstName(name);
    personalInfo.setLastName(surname);
    personalInfo.setGender(mf);
    personalInfo.setSsn(s_s_n);
}

void InstructorProfile::setInstructorInfo(long id, std::string name,
                                          Course* cses)
{
    instructorInfo.setEmployeeID(id);
    instructorInfo.setOfficeNum(name);
    instructorInfo.setCourses(cses);
}

main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <iostream>
#include <fstream>
#include <limits>
#include <string>
#include <vector>
#include "InstructorProfile.h"
#include "Course.h"

using std::cin;
using std::cout;
using std::endl;

const std::string FILENAME("example.txt");

void pause();
void createMockInstrProf();
void writeOnFile(std::string filename, InstructorProfile& prof);
void destroyFileContent(std::string filename);
std::vector<InstructorProfile> readFromFile(std::string filename,
                               std::vector<InstructorProfile>& inpro);

int main()
{
    // Only invoke this function if you don't have
    // an example.txt file:
    createMockInstrProf();

    std::vector<InstructorProfile> instructorDirectory;
    readFromFile(FILENAME, instructorDirectory);

    // Examples...
    cout << instructorDirectory.at(2).getInstructorInfo().getOfficeNum()
         << endl;
    // ...or...
    InstructorProfile inpro;
    inpro = instructorDirectory.at(1);
    cout << inpro.getPersonalInfo().getLastName() << endl;

    pause();
    return 0;
}

void pause()
{
    std::cout << "\nPress ENTER to continue..." << std::endl;
    std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
}

void createMockInstrProf()
{
    InstructorProfile mock_profile;

    // Mock InstructorProfile n. 1
    mock_profile.setPersonalInfo("John", "Smith", 'M', 13);
    Course courses[3];
    courses[0].setCourseNumber(10);
    courses[0].setCourseName("Cooking");
    courses[0].setNumberOfCredits(1000);

    courses[1].setCourseNumber(11);
    courses[1].setCourseName("Sewing");
    courses[1].setNumberOfCredits(1001);

    courses[2].setCourseNumber(12);
    courses[2].setCourseName("Knitting");
    courses[2].setNumberOfCredits(1002);

    mock_profile.setInstructorInfo(1, "Office13", courses);

    writeOnFile(FILENAME, mock_profile);


    // Mock InstructorProfile n. 2
    mock_profile.setPersonalInfo("Ann", "Johnsons", 'F', 14);
    courses[0].setCourseNumber(13);
    courses[0].setCourseName("Sleeping");
    courses[0].setNumberOfCredits(1013);

    courses[1].setCourseNumber(14);
    courses[1].setCourseName("Googling");
    courses[1].setNumberOfCredits(1014);

    courses[2].setCourseNumber(15);
    courses[2].setCourseName("Yawning");
    courses[2].setNumberOfCredits(1015);

    mock_profile.setInstructorInfo(2, "Office14", courses);

    writeOnFile(FILENAME, mock_profile);

    // Mock InstructorProfile n. 3
    mock_profile.setPersonalInfo("Simone", "Simpson", 'F', 15);
    courses[0].setCourseNumber(16);
    courses[0].setCourseName("Skiving");
    courses[0].setNumberOfCredits(1016);

    courses[1].setCourseNumber(17);
    courses[1].setCourseName("Loafing");
    courses[1].setNumberOfCredits(1017);

    courses[2].setCourseNumber(18);
    courses[2].setCourseName("Dawdling");
    courses[2].setNumberOfCredits(1015);

    mock_profile.setInstructorInfo(3, "Office15", courses);

    writeOnFile(FILENAME, mock_profile);
}

void writeOnFile(std::string filename, InstructorProfile& prof)
{
    std::ofstream ofstr(filename, std::ios::out | std::ios::app);
    ofstr << prof.getPersonalInfo() << std::endl;
    ofstr << prof.getInstructorInfo() << std::endl;
    ofstr.close();
}

void destroyFileContent(std::string filename)
{
    std::ofstream ofstr(filename);
    ofstr.close();
}

std::vector<InstructorProfile> readFromFile(std::string filename,
                               std::vector<InstructorProfile>& inpro)
{
    std::ifstream ifstr(filename);

    InstructorProfile prof;

    // Untill we reach the end of the file...
    for(std::string row; std::getline(ifstr, row, '\n'); ) {
        // If we find [Person], we need the following lines,
        // but only the part after ":".
        std::string::size_type pos;
        if(row == "[Person]"){
            Person person;

            // Let's extract firstName
            std::getline(ifstr, row, '\n');
            if((pos=row.find_last_of(":")) != std::string::npos)
                person.setFirstName(row.erase(0, pos+2));

            // Let's extract lastName
            std::getline(ifstr, row, '\n');
            if((pos=row.find_last_of(":")) != std::string::npos)
                person.setLastName(row.erase(0, pos+2));

            // Let's extract gender
            std::getline(ifstr, row, '\n');
            if((pos=row.find_last_of(":")) != std::string::npos){
                row.erase(0, pos+2);
                person.setGender(row.at(0));
            }

            // Let's extract Ssn
            std::getline(ifstr, row, '\n');
            if( (pos=row.find_last_of(":")) != std::string::npos ) {
                row.erase(0, pos+2);
                person.setSsn(std::stol(row));
            }

            prof.setPersonalInfo(std::move(person));
//            cout << "prof.getPersonalInfo().getFirstName(): "
//                 << prof.getPersonalInfo().getFirstName()
//                 << "\nprof.getPersonalInfo().getLastName(): "
//                 << prof.getPersonalInfo().getLastName()
//                 << "\nprof.getPersonalInfo().getGender(): "
//                 << prof.getPersonalInfo().getGender()
//                 << "\nprof.getPersonalInfo().getSsn(): "
//                 << prof.getPersonalInfo().getSsn() << endl;

        // Otherwise, if we find [Instructor], we need the following lines,
        // but only the part after ":".
        } else if(row == "[Instructor]") {
            Instructor instru;
            // std::string::size_type pos is still visible here

            //Let's extract long employeeID
            std::getline(ifstr, row, '\n');
            if( (pos=row.find_last_of((":"))) != std::string::npos ) {
                row.erase(0, pos+2);
                instru.setEmployeeID(std::stol(row));
            }

            //Let's extract std::string officeNum
            std::getline(ifstr, row, '\n');
            if( (pos=row.find_last_of((":"))) != std::string::npos )
                instru.setOfficeNum(row.erase(0, pos+2));

            //Let's extract Course courses[MAXCOURSES]
            std::getline(ifstr, row, '\n'); // Let's skip "Courses:"
            Course cou[3];
            for(int i{0}; i<3; i++){
                std::getline(ifstr, row, '\n');
                if( (pos=row.find_first_of("0123456789")) != std::string::npos ){
                    row.erase(0, pos);
                    pos = row.find_first_of(' ');
                    // get the first number: long courseNumber
                    std::string tmp = row.substr(0, pos);
                    cou[i].setCourseNumber(std::stol(tmp));

                    // Delete first number + leading space
                    row.erase(0, pos+1);
                    pos = row.find_first_of(' ');
                    // get the string in the middle: std::string courseName
                    tmp = row.substr(0, pos);
                    cou[i].setCourseName(tmp);

                    // delete string in the middle + leading space
                    row.erase(0, pos+1);
                    // get the last number: int numberOfCredits
                    cou[i].setNumberOfCredits(std::stoi(row));
                }
            }
            instru.setCourses(cou);

            prof.setInstructorInfo(std::move(instru));
//            cout << "prof.getInstructorInfo().getEmployeeID(): "
//                 << prof.getInstructorInfo().getEmployeeID()
//                 << "\nprof.getInstructorInfo().getOfficeNum(): "
//                 << prof.getInstructorInfo().getOfficeNum()
//                 << "\nprof.getInstructorInfo().getCourses(): ";
//            const Course* displaycou = prof.getInstructorInfo().getCourses();
//            for(int i{0}; i<3; i++)
//                cout << displaycou[i].getCourseNumber() << ' '
//                     << displaycou[i].getCourseName() << ' '
//                     << displaycou[i].getNumberOfCredits() << "; ";
//            cout << endl << endl;
        }

        inpro.push_back(prof);
    } // end for

    ifstr.close();

    return inpro;
}

Thank you !!! It helped a lot!
@Enoizat

It's nice to help out, the trouble is that you have completed the assignment - that was supposed to be the OP's job.

Some of the following are the fault of whoever set the assignment , some of it is personal preference :+)

Always pass std::string (or any class ) by reference. In the implementation (for classes or free functions) mark the parameters as const

1
2
3
4
5
6
7
class Person
{
public:
    Person();
    
//parametrs not const here, so the user doesn't think they have to make them const before calling the function
    Person(std::string firstNameArg, std::string surnameArg, char genderArg, unsigned long s_s_nArg);




1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Function definitions
// const and reference here
Person::Person(const std::string& firstNameArg, // should avoid camelCase, can have 
                                                 // lower_case_with_underscore or PascalCase
            const std::string& surnameArg, 
            const char genderArg, // a better name
            const unsigned long s_s_nArg) : // what does ssn mean? Social Security Number ? But I shouldn't have
                                            // to think about it .... I might assume something incorrectly

    firstName{name}, 
    lastName{surname}, 
    gender{genderArg}, 
    ssn{s_s_n}
{}

I like to name the parameters the same as the member variable name, but with Arg on the end, as in surnameArg, surname.

Ideally Name would be a class of it's own.

The Course number and things like empoyeeID should probably be unsigned. Looks like the teacher forgot :+)
Last edited on
> Always pass std::string (or any class ) by reference.

This is canonical C++11 (exploit move semantics to the hilt):
1
2
3
4
5
6
7
8
struct person
{
    // std::is_nothrow_move_constructible<std::string>::value == true
    person( std::string long_name /* ... */ ) noexcept : name( std::move(long_name) ) /* .. */  {}

    std::string name ;
    // ...
};
Hi JLBorges,

Always new things to learn :+)

Just wondering whether one could have an rvalue reference parameter in the implementation - would that force the use of the move constructor for std::string?

1
2
3
4
5
6
7
8
9
struct person
{
    // std::is_nothrow_move_constructible<std::string>::value == true
    person(std::string long_name /* ... */ ) 
    std::string name ;
    // ...
};

person::person (const std::string&& long_name) noexcept : name(long_name) {}


I should try this myself, but I need to go out soon - will have a go once I get back in 5 hours time :+)

Regards
closed account (48T7M4Gy)
http://stackoverflow.com/questions/3106110/what-are-move-semantics
@TheIdeasMan, thank you for your advice.
@Enoizat

I haven't tested that yet or claim to know that it works or is best. In that scenario, trust JLBorges - he is the expert :+)
tIM: I've made some attempts to try and test your hypothesis - for string literal the move method is called but for variants of std::string arguments the copy method is called each time, even if the type of the argument exactly matches that of the move method. Deleting the copy method actually produces a compile time error 'use of deleted function' etc:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <utility>

struct Person
{
    std::string m_name;
    Person(const std::string& name) : m_name(name){std::cout << "Copy \n";} /*= delete;*/
    Person(const std::string&& name) : m_name(std::move(name)){std::cout << "Move \n";}
    //Person(const std::string&& name) : m_name (name) {std::cout << "Move \n";}//is this really a move method?
};

int main()
{
    Person a("John");//the string literal calls move method
    const std::string&& name = "Jane";//same result for const std::string, const std::string&&
    Person b(name);
}

This should (kind-of) make sense according to
(m)oving a value out of an object generally modifies the object, so the standard should not permit const objects to be passed to functions (such as move constructors) that could modify them
- Effective Modern C++, Item 23
The reason I say kind-of makes sense is (a) there are no compiler warnings and (b) when I run the program with the following:
http://coliru.stacked-crooked.com/a/6e847dca15d190e4
... the move method is called in both instances, including on the const std::string& variables - this bit puzzles me, how come the move method gets to work on a const object?
Last edited on
gunnerfunner wrote:
... the move method is called in both instances, including on the const std::string& variables - this bit puzzles me, how come the move method gets to work on a const object?


When I run it on cpp.sh with c++14, all 3 warnings on, any level of optimization, I get this:

Move
Copy


cpp.shell uses gcc 4.9.2

Edit: Line 8 is commented out in Coliru :+)


I just wonder about the difference in efficiency between the two anyway. Doesn't line 8 copy the pointer(reference), and the move is conceptually doing the same thing, but leaving the original in an empty state? It may be we don't to move anyway - we want to preserve the value of the variable in main.

In other words: what is to be gained by exploiting the move?

Perhaps if I changed my original advice, would that be suitable for beginners?

TheIdeasMan wrote:
Always pass std::string (or any class ) by reference.


Edit2:
Btw, I meant for the std::string move constructor to be implicitly called, not to explicitly call std::move.
Last edited on
> what is to be gained by exploiting the move?

In most cases (eg. std::vector), only performance.

In some cases (eg. std::ifstream), the type may be non-copyable, but moveable

Re. performance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <memory>
#include <vector>

int main()
{
    struct A
    {
        A() = default ;

        A( int v )
        {
            std::uninitialized_fill( array, array+SZ, v ) ;
            std::cout << "construct from int: fill array of " << SZ << " int\n" ;
        }

        A( const A& that )
        {
            std::uninitialized_copy( that.array, that.array+SZ, array ) ;
            std::cout << "copy construct: copy " << SZ << " int\n" ;
        }

        #ifdef ENABLE_MOVE
        A( A&& that ) noexcept : array(that.array)
        {
            that.array = nullptr ;
            std::cout << "move construct: grab the array from rvalue\n" ;
        }
        #endif // ENABLE_MOVE

        A& operator= ( A that ) noexcept // unifying assignment operator
        {
            using std::swap ;
            swap( array, that.array ) ;
            return *this ;
        }

        ~A() { if(array) { std::cout << "deallocate array of " << SZ << " int\n" ; delete[] array ; } }

        // unrealistic, only for exposition
        enum { SZ = 1'000'000 } ;
        int* array { ( (std::cout << "allocate array of " << SZ << " int\n"),
                        new int[SZ] {} ) };
    };

    struct B
    {
        #ifdef ENABLE_MOVE
            explicit B( A arg = {} ) : aa( std::move(arg) ) {}
        #else
            explicit B( const A& arg = {} ) : aa(arg) {}
        #endif // ENABLE_MOVE

        A aa ;
    };

    std::cout << "\n1.\n" ;
    A a ;

    std::cout << "\n2.\n" ;
    B b1(a) ;

    std::cout << "\n3.\n" ;
    B b2(23) ;

    std::cout << "\n4.\n" ;
    const auto fn = [] () -> A { return 0 ; } ;
    B b3( fn() ) ;

    std::cout << "\n5.\n" ;
    B b4( std::move(a) ) ;

    std::cout << "\n6.\n" ;
    std::vector<B> vec(5) ;
    
    std::cout << "\n7.\n" ;
    vec.reserve(100) ;

    std::cout << "\n\nend main: destroy the five objects in vector, a, b1, b2, b3, b4\n" ;
}

Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
with ENABLE_MOVE
++++++++++++++++++

1.
allocate array of 1000000 int

2.
allocate array of 1000000 int
copy construct: copy 1000000 int
move construct: grab the array from rvalue

3.
allocate array of 1000000 int
construct from int: fill array of 1000000 int
move construct: grab the array from rvalue

4.
allocate array of 1000000 int
construct from int: fill array of 1000000 int
move construct: grab the array from rvalue

5.
move construct: grab the array from rvalue
move construct: grab the array from rvalue

6.
allocate array of 1000000 int
move construct: grab the array from rvalue
allocate array of 1000000 int
move construct: grab the array from rvalue
allocate array of 1000000 int
move construct: grab the array from rvalue
allocate array of 1000000 int
move construct: grab the array from rvalue
allocate array of 1000000 int
move construct: grab the array from rvalue

7.
move construct: grab the array from rvalue
move construct: grab the array from rvalue
move construct: grab the array from rvalue
move construct: grab the array from rvalue
move construct: grab the array from rvalue


end main: destroy the five objects in vector, a, b1, b2, b3, b4
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
without ENABLE_MOVE
+++++++++++++++++++

1.
allocate array of 1000000 int

2.
allocate array of 1000000 int
copy construct: copy 1000000 int

3.
allocate array of 1000000 int
construct from int: fill array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int

4.
allocate array of 1000000 int
construct from int: fill array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int

5.
allocate array of 1000000 int
copy construct: copy 1000000 int

6.
allocate array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int
allocate array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int
allocate array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int
allocate array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int
allocate array of 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int

7.
allocate array of 1000000 int
copy construct: copy 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
allocate array of 1000000 int
copy construct: copy 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int


end main: destroy the five objects in vector, a, b1, b2, b3, b4
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int
deallocate array of 1000000 int

http://coliru.stacked-crooked.com/a/72310eb4209bba22
Line 8 is commented out in Coliru

indeed, that was the whole point of the Coliru exercise - to see if a const std::string could be passed to a method that std::move's it when the corresponding copy method is commented out and that indeed seems to happen which means that the const std::string is no longer guaranteed to be const! This occurs also at cpp.sh under similar conditions. That's the bit I find puzzling though I could have been clearer about the motivation behind sending the Coliru link

meant for the std::string move constructor to be implicitly called

Item 23, Effective Modern CPP example shows that std::string copy ctor is called when the argument of a class ctor is const std::string:
1
2
3
4
5
6
7
8
class Annotation {
public:
    explicit Annotation(const std::string text)
    : value(std::move(text){}
    //...
private:
    std::string value;
};


In the Annotation constructor’s member initialization list, the result of std::move(text) is an rvalue of type const std::string. That rvalue can’t be passed to std::string’s move constructor, because the move constructor takes an
rvalue reference to a non-const std::string. The rvalue can, however, be passed to the copy constructor, because an lvalue-reference-to-const is permitted to bind to a const rvalue. The member initialization therefore invokes the copy constructor in std::string, even though text has been cast to an rvalue! Such behavior is essential to maintaining const-correctness.
Topic archived. No new replies allowed.