directory

  • 1. Destructor (Destructor)
    • Create Employee objects on the heap and stack (functional scope and nested scope) and observe the behavior of the destructor
  • 2, My Friend
    • 1. Why do you need friends
    • Friend functions and friend classes
    • 3. Some questions about friends
  • (3) Copy Constructor
    • Copy the structure
    • Implicitly declared copy constructors
    • Copy and create Employee objects on the heap and stack
  • 4. Deep and shallow copies
    • 1, Customizing Copy Constructor
    • 2. Questions to be solved

1. Destructor (Destructor)

Destructors are the opposite of constructors.

Note that overloaded functions are distinguished by the number and order of function arguments. Destructors cannot be overloaded without arguments.

Create Employee objects on the heap and stack (functional scope and nested scope) and observe the behavior of the destructor

#include<iostream>
#include<string>
using namespace std;

class Date {
private:
    int year = 2019, month = 1, day = 1;
public:
    int getYear(a) { return year; }
    int getMonth(a) { return month; }
    int getDay(a) { return day; }
    void setYear(int y) { year = y; }
    void setMonth(int m) { month = m; }
    void setDay(int d) { day = d; }
    Date() = default;
    Date(int y, int m, int d) :year(y), month(m), day(d) {
        std::cout << "Date" << toString() << std::endl;
    }
    std::string toString(a) {
        return (std::to_string(year) + The '-' + std::to_string(month) + The '-' + std::to_string(day)); }};enum class Gender {
    male,
    female,
};

class Employee {
private:
    std::string name;
    Gender gender;
    Date* birthday;
public:
    // Static member used to count the number of employee objects
    static int numberOfObjects;
    void setName(std::string name) { this->name = name; }
    void setGender(Gender gender) { this->gender = gender; }
    void setBirthday(Date birthday) { *(this->birthday) = birthday; }
    std::string getName(a) { return name; }
    Gender getGender(a) { return gender; }
    Date getBirthday(a) { return *birthday; }
    std::string toString(a)
    {
        return (name +( (gender == Gender::male ? std::string(" male ") : std::string(" female ") )+ birthday->toString()));
    }
    // Parameter constructor
    Employee(std::string name,Gender gender,Date birthday):name{name},gender{gender}{
        // Add the number of objects +1 for each construction
        numberOfObjects++;
        // Note that the destructor of the new object is delete
        // A new Date object is constructed on the heap and stored in the data member, passing the new data address to the birthday variable of the current object
        this->birthday = new Date(birthday);
        std::cout << "Now there are : " << numberOfObjects << " employees" << std::endl;
    }
    // Default constructor
    Employee() :Employee("Alan",Gender::male,Date(2000.4.1)) {}// destructor
    ~Employee()
    {
        // When destructing an object, the number of members is -1
        numberOfObjects--;
        // Release the variables constructed on the heap. There is no shallow-copy function, so no special attention is required
        delete birthday;
        birthday = nullptr;
        std::cout << "> < span style =" max-width: 100%; clear: both; << numberOfObjects << " employees"<< std::endl; }};int Employee::numberOfObjects = 0;
// Create Employee objects on the heap and stack (functional scope and nested scope) and observe the behavior of the destructor
int main(a)
{
    Employee e1;
    std::cout << e1.toString() << std::endl;
    Employee* e2 = new Employee{"John",Gender::male,Date(1990.3.2)}; std::cout << e2->toString() << std::endl;
    //e3 is an object defined in an inline scope, outside which it is destructed.
    {
        Employee e3{ "Alice",Gender::female,{1989.2.14}}; std::cout << e3.toString() << std::endl;
    }
    std::cout << "Now there are : " << Employee::numberOfObjects << " employees" << std::endl;
    return 0;
}
Copy the code

E3 is an object defined in an embedded scope, outside which it is destructed.

2, My Friend

1. Why do you need friends

1. Private members cannot be accessed from outside the class. 2

Friend functions and friend classes

Friends can be defined outside the class, but must be declared inside the class.

In the following example, both the Kid class and the print function have direct access to the private member of the Date class

class Date {
private:
  int year{ 2019 } , month{ 1 };
  int day{ 1 };
public:
  friend class Kid;
  friend void print(const Date& d);
};
void print(const Date& d) {
  cout << d.year << "/" << d.month 
       << "/" << d.day << endl;
}
class Kid {
private:
  Date birthday;
public:
  Kid() { 
    cout << "I was born in "<< birthday.year << endl; }};int main(a) {
  print(Date());
  Kid k;
  cin.get(a); }Copy the code

3. Some questions about friends

Can two classes be friends of each other? 2. Is there such a thing as friend or something similar in other object-oriented programming languages? 3. A class can have friends that can access private/protected members of the class. So, can a function have friends that access local variables in the function?

1. Yes. We can declare Screen as a friend of the Window class, and the Window class as a friend of Screen. The member functions of the two classes can then access each other’s private and protected members. 2. No 3. No

(3) Copy Constructor

Copy the structure

Copy constructor: The copy constructor that initializes an object from another of its kind can be abbreviated to copy ctor, or cp ctor. How to declare the copy constructor (copy ctor)

Circle (Circle&);
Circle (const Circle&);
Copy the code
Circle c1( 5.0 ); 
Circle c2( c1 );    //c++03
Circle c3 = c1;     //c++03
Circle c4 = { c1 }; //c++11
Circle c5{ c1 };    //c++11
Copy the code

Copy constructor with additional default arguments

class X {  // from C++11 standard: section 12.8
// ...
public:
  X(const X&, int = 1);
};
X b(a, 0); // calls X(const X&, int);
X c = b;   // calls X(const X&, int);
Copy the code

Two objects, obj1 and obj2, are already defined. Then a statement of the form:

obj1 = obj2;

Instead of calling the copy constructor, object assignment is called.

Instead, the following statement:

AClass aObject = bObject; // bObject is also an AClass object

Although there is an “equal sign (=)”, because the object is “assigned” when it is defined, the “equal sign (=)” is interpreted as initialization and the copy constructor needs to be called.

Implicitly declared copy constructors

In general, if the programmer does not write a copy constructor, the compiler will automatically generate one. The automatically generated copy constructors are called implicitly declared/defined copy constructors. In general, the implicitly declared copy Ctor simply copies each data field in the object as a parameter into the new object.

Copy and create Employee objects on the heap and stack

#include<iostream>
#include<string>
using namespace std;

class Date {
private:
    int year = 2019, month = 1, day = 1;
public:
    int getYear(a) { return year; }
    int getMonth(a) { return month; }
    int getDay(a) { return day; }
    void setYear(int y) { year = y; }
    void setMonth(int m) { month = m; }
    void setDay(int d) { day = d; }
    Date() = default;
    Date(int y, int m, int d) :year(y), month(m), day(d) {
        std::cout << "Date" << toString() << std::endl;
    }
    std::string toString(a) {
        return (std::to_string(year) + The '-' + std::to_string(month) + The '-' + std::to_string(day)); }};enum class Gender {
    male,
    female,
};

class Employee {
private:
    std::string name;
    Gender gender;
    Date* birthday;
public:
    // Static member used to count the number of employee objects
    static int numberOfObjects;
    void setName(std::string name) { this->name = name; }
    void setGender(Gender gender) { this->gender = gender; }
    void setBirthday(Date birthday) { *(this->birthday) = birthday; }
    std::string getName(a) { return name; }
    Gender getGender(a) { return gender; }
    Date getBirthday(a) { return *birthday; }
    std::string toString(a)
    {
        return (name + ((gender == Gender::male ? std::string(" male ") : std::string(" female ")) + birthday->toString()));
    }
    // Parameter constructor
    Employee(std::string name, Gender gender, Date birthday) :name{ name }, gender{ gender }{
        // Add the number of objects +1 for each construction
        numberOfObjects++;
        // Note that the destructor of the new object is delete
        // A new Date object is constructed on the heap and stored in the data member, passing the new data address to the birthday variable of the current object
        this->birthday = new Date(birthday);
        std::cout << "Now there are : " << numberOfObjects << " employees" << std::endl;
    }
    // Default constructor
    Employee() :Employee("Alan", Gender::male, Date(2000.4.1)) {}
    // Copy the constructor
    Employee(const Employee& e1) {
        this->birthday = e1.birthday;
        this->name = e1.name;
        this->gender = e1.gender;
        // The number also needs +1
        numberOfObjects++;
        std::cout << "Employee(const Employee&) is invoked" << std::endl;
    }
    // destructor
    ~Employee()
    {
        // When destructing an object, the number of members is -1
        numberOfObjects--;
        // Note that if the destructor is a shallow-copy function and the copied object has already been deleted, the data need not be deleted
        //delete birthday;
        //birthday = nullptr;
        std::cout << "> < span style =" max-width: 100%; clear: both; << numberOfObjects << " employees"<< std::endl; }};int Employee::numberOfObjects = 0;
// Create Employee objects on stack and heap respectively
int main(a)
{
    // Default construction
    Employee e1;
    std::cout << e1.toString() << std::endl;
    // Copy construct
    Employee e2 = {e1};
    std::cout << e2.toString() << std::endl;
    // Build on the heap
    Employee* e3 = new Employee{ "John",Gender::male,Date(1990.3.2)}; std::cout << e3->toString() << std::endl;
    std::cout << std::endl;
    std::cout << "Now there are : " << Employee::numberOfObjects << " employees" << std::endl;
    return 0;
}
Copy the code

4. Deep and shallow copies

Because of the copy function above, we are assigning all the data members of an object to a new object, so there is a problem. If a data member is of type pointer (address), then the address of the data in our newly constructed object is the same. For non-address data, this problem is not present. I feel that this is also a vulnerability of the copy function, generally my intuitive understanding of the copy is deep copy rather than shallow copy. Shallow copy: A data field is a pointer that copies only the address of the pointer, not what the pointer points to. Shallow copies occur in two cases:

When creating a new object, the default assignment operator is used when the class’s implicit/default constructor is called to assign to an existing object

Note: There is A pointer P in class A that points to A plug-in object B (b is an object of type B). If class A has no pointer member P in it, then don’t talk about shallow copy. We now have an object a1 of class A (a1’s pointer P points to the plugged-in object B1). Creates a copy a2 of A1 using copy construction.

(1) If only the value of A1. P (which is an address) is copied to a2. P, it is a shallow copy. After a shallow copy, both A1. p and a2.p point to b1 (2). And copy the value of B1 to b2, which is a deep copy

Employee e1{"Jack".Date(1999.5.3),  Gender::male};
Employee e2{"Anna".Date(2000.11.8), Gender:female};
Employee e3{ e1 };  //cp ctor performs a one-to-one member copy
Copy the code

The copy constructor of Employee is called when the E3 object is created.

After the above code is executed, the e3. Birthday pointer points to the Date object pointed to by E1. birthday, which causes e1 to be modified and e2 to be modified.

1, Customizing Copy Constructor

How to deep copy

(1) Write your own copy constructors, not using the (default) compiler implicitly generated copy constructors (2) overload assignment operators, not using the (default) compiler implicitly generated assignment operator functions

At this point, we generate a new object based on the copied object, and then assign this object to the copied object.

class Employee {
public:
    // Employee(const Employee &e) = default; // Shallow copy of cTOR
  Employee(const Employee& e){    // Deep copy cTOR
    birthdate = new Date{ e.birthdate };
  } // ...
}
Employee e1{"Jack".Date(1999.5.3), Gender::male};
Employee e2{"Anna".Date(2000.11.8),, Gender:female};
Employee e3{ e1 };  //cp ctor deep copy
Copy the code

2. Questions to be solved

A question about shallow-copy objects and their destructionIf we use the shallow-copy constructor:

Employee(const Employee& e1) {

this->birthday = e1.birthday; this->name = e1.name; this->gender = e1.gender; // the number also needs to be +1 numberOfObjects++; std::cout << "Employee(const Employee&) is invoked" << std::endl; }Copy the code

Then we use the shallow-copy constructor in the main function, since our parameter constructor is creating a new data object in the heap

Employee(STD ::string name, Gender Gender, Date birthday) :name{name}, Gender {Gender}{// increment, +1 numberOfObjects++; // A new Date object is constructed on the heap, This ->birthday = new Date(birthday); this->birthday = new Date(birthday); std::cout << "Now there are : " << numberOfObjects << " employees" << std::endl; }Copy the code

So in the destructor we delete this data

    delete birthday;

    birthday = nullptr;
Copy the code

So here’s the question:

The compiler will report an error when deleting an object that points to a destruct, meaning birthday was deleted. I have already asked this question in moOCs, and I will update it when the teacher replies.

Solution reply:

In general, if a normal constructor allocates memory for a class member, then both the copy constructor and overloaded assignment operator functions need to perform deep copies.