Table of contents
The 6 default member functions of the class
Note that the code of this article does not add the following content by default
#include <iostream>
using std::endl;
using std::cout;
The 6 default member functions of the class
The following is an empty class,
class Date {};
Constructor
concept
characteristic
1. The function name is the same as the class name.2. No return value.3. The compiler automatically calls the corresponding constructor when the object is instantiated .4. The constructor can be overloaded.
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//构造函数是可以写多个的
//Date() //因为构造函数支持函数重载,所以可以合并到一起
//{
// _year = 1;
// _month = 1;
// _day = 1;
//}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 10, 8); // 调用带参的构造函数
Date d2(2022, 10, 9);
Date d3; //不可以加 () // 调用无参构造函数
//无参的不要像如下这样子写
//Date d4(); //这样子在VS里面会报警告,但是在别的编译器中可能会直接报错
//Date func(); //上面这样子写就类似于函数的声明了,会出错的
d1.Print();
d2.Print();
d3.Print();
return 0;
}
Constructor usage
When in use, the compiler will automatically call the constructor to do some work, which can avoid many errors caused by forgetting, such as forgetting Inint ( initialization) , forgetting destroy (release memory)
Built-in types: int / char / double ... pointers (pointers of custom types are also counted)
Custom types: class / struct ... Stack / queue / Person
The built-in type does not handle, and the custom type will call its default constructor
Analyze the code below
class A
{
public:
A()
{
_a = 0;
cout << "A()构造函数" << endl;
}
private:
int _a;
};
class Date
{
public:
// 没有写构造函数
//Date(int year = 1, int month = 1, int day = 1)
//{
// _year = year;
// _month = month;
// _day = day;
//}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 内置类型
int _year; // 年
int _month; // 月
int _day; // 日
// 自定义类型
A _aa;
};
int main()
{
Date d1;
d1.Print();
return 0 ;
}
Through the above code, it is found that the built-in type will call the default constructor, but it will not be processed (initialized), because the custom type will call the constructor
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "Stack(int capacity = 4)" << endl;
_a = (int*)malloc(sizeof(int) * capacity);
if (_a == nullptr)
{
perror("malloc fail");
exit(-1);
}
_top = 0;
_capacity = capacity;
}
~Stack() //这里是一个析构函数, 函数名与类名一致,但是前面需要加上 ~(C语言中的取反)
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
void Push(int x)
{
// ....
// 扩容
_a[_top++] = x;
}
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue {
public:
void push(int x)
{
_pushST.Push(x);
}
Stack _pushST; //自动调用了 Stack 里面的构造函数
Stack _popST;
//size_t _size;
};
int main()
{
MyQueue mq;
mq.push(1);
mq.push(2);
return 0;
}
When there is a default constructor and the requirements are met, there is no need to write another function, and it can be called directly (the destructor will be introduced below)
Based on the above inconveniences, it was optimized in C++11 later, and a patch was applied to directly give the default value (default value)
Then with this patch, the above code can be written as follows
Note that these are all default values, not initialization, and it does not open up space for it. The above described it as a blueprint, which has methods but no entities.
The same feature that conforms to the default value, when there is no given value, it will be initialized directly with the default value, and when there is a value, the default value will not be used, as follows, it will not be initialized to 0, while all initialized to 1
7. Both the parameterless constructor and the default constructor are called default constructors, and there can only be one default constructor.Note: No-argument constructors, full default constructors, and constructors that we did not write by default generated by the compiler can all be consideredis the default constructor.
destructor
concept
Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself, and the local object destruction is done by the compiler. When the object is destroyed, it will automatically call the destructor to complete the cleanup of resources in the object.
characteristic
A destructor is a special member function whose characteristics are as follows:
1. The name of the destructor is to add characters before the class name ~2. No parameters and no return value type.3. A class can only have one destructor. If not explicitly defined, the system will automatically generate a default destructor.Note: Destructors cannot be overloaded4. When the object life cycle ends, the C++ compilation system automatically calls the destructor
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
_array[_size] = data;
_size++;
}
~Stack() //析构函数,在对象生命周期结束时自动调用
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity = 4;
int _size = 0;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
}
int main()
{
TestStack();
return 0;
}
//析构函数 补
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
The output of the above is as follows
In the main method, no object of the Time class is directly created at all. Why is the destructor of the Time class called at the end?
Because: the Date object d is created in the main method, and d contains 4 member variables, among which _year, _month, and _day are built-in type members, and resource cleaning is not required when destroying. Finally, the system can directly reclaim its memory ; and _t is an object of the Time class, so when d is destroyed, the _t object of the Time class contained in it must be destroyed, so the destructor of the Time class must be called.
However: the destructor of the Time class cannot be called directly in the main function. What actually needs to be released is the Date class object, so the compiler will call the destructor of the Date class. If Date is not explicitly provided, the compiler will give the Date class Generate a default destructor, the purpose is to call the destructor of the Time class inside it, that is, when the Date object is destroyed, it is necessary to ensure that each custom object inside it can be destroyed correctly and the Time class is not directly called in the main function Destructor, but explicitly call the default destructor generated by the compiler for the Date class.
Note: the destructor of that class is called when an object of that class is created, and the destructor of that class is called when an object of that class is destroyed.
Example case (from Topic 232. Implementing Queues with Stacks - LeetCode )
Of course, sometimes there is no need to write a destructor, because the compiler will automatically generate it when there is no destructor. For example, there is no need to write the date class above. It depends on what you need to use.
copy construction
concept
Copy constructor : There is only a single formal parameter , which is a reference to the object of this class type ( usually const decoration is commonly used ) , and is automatically called by the compiler when creating a new object with an existing class type object .
Construction means initialization, then copy construction means copy initialization
Some places call it copy constructor
feature
The copy constructor is also a special member function with the following characteristics :1. The copy constructor is an overloaded form of the constructor .2. There is only one parameter of the copy constructor and it must be a reference to a class type object . If the value-passing method is used, the compiler will report an error directly, because it will cause infinite recursive calls.
The built-in type (provided by the compiler, the compiler knows the built-in type well) so there is no copy constructor, but the custom type (compare copy) needs a copy constructor to help complete
The following code
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d) //使用 &(无限循环访问构造函数) 并且要加上 const (防止写反,导致被反向赋值)
{
cout << "Date 拷贝构造" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
// 形参加const,防止写反了,下面问题就可以检查出来
//d._day = _day;
}
private:
int _year;
int _month;
int _day;
};
void Func1(Date d)
{
cout << "Func1()" << endl;
}
void Func2(Date& d)
{
cout << "Func2()" << endl;
}
int main()
{
Date d1(2022, 9, 22); // 构造 - 初始化
Func1(d1);
Func2(d1);
// 拷贝一份d1
//Date d2(d1); // 拷贝构造 -- 拷贝初始化 会调用拷贝构造函数
return 0;
}
Without & will call the constructor infinitely
Without const, the following errors are prone to occur
Another thing to note is that the & here can theoretically be replaced by pointers, but using it will make the code look untidy. More importantly, using pointers to complete the operation is no longer a copy construction. , should call it the constructor
// 这里会发现下面的程序会崩溃掉?这里就需要写一个深拷贝去解决。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
size_t _size = 0;
size_t _capacity = 0;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
Stack s2(s1); //浅拷贝
return 0;
}
It will be found that the program crashes (the destructor destroys the same space twice), here, according to the nature of the stack, the latter definition will be destructed first (last in, first out), s2 will be destructed first, and then s1 will be destructed . However, since the destructor automatically generated by the compiler is a shallow copy, the two pointers point to the same space, so the program will crash directly
deep copy
//深拷贝
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
_array[_size] = data;
_size++;
}
Stack(const Stack& st)
{
_array = (DataType*)malloc(sizeof(DataType) * st._capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
memcpy(_array, st._array, sizeof(DataType) * st._size); //利用memcpy进行拷贝,利用 st._size(数据的个数)更好点
_size = st._size;
_capacity = st._capacity;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
size_t _size = 0;
size_t _capacity = 0;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
Stack s2(s1); //深拷贝
s2.Push(10);
return 0;
}
Surveillance found that the two are no longer pointing to the same space, and the two do not interfere with each other
1. Create a new object using an existing object2. The function parameter type is a class type object3. The return value type of the function is a class type object
operator overloading
The next article will mention assignment operator overloading
concept
C++ introduces operator overloading to enhance the readability of the code . Operator overloading is a function with a special function name , and also has its return value type, function name, and parameter list. The return value type and parameter list are similar to ordinary functions.The function name is: the keyword operator followed by the operator symbol that needs to be overloaded .Function prototype: return value type operator operator ( parameter list )
Notice:
1. You cannot create new operators by connecting other symbols: such as operator@2. Overloaded operators must have a class type parameter (operators that cannot change built-in types)3. The meaning of the operator used for built-in types cannot be changed, for example: the built-in integer + cannot change its meaning4. When overloaded as a class member function, its formal parameters seem to be 1 less than the number of operands , because the first parameter of the member function is implicitHidden this.* :: sizeof ?: . Note that the above 5 operators cannot be overloaded. This often appears in written multiple choice questions.The first one is relatively rare and is an ancient operator, but it still needs to be paid attention to
The following is an operator overloading
//运算符的重载
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 我们不写,编译器生成的默认拷贝构造函数
//private:
int _year;
int _month;
int _day;
};
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
int main()
{
Date d1(2022, 9, 22);
Date d2(2023, 1, 1);
//d1 > d2;
cout << (d1 == d2) << endl; // 转换成operator==(d1, d2);
d1 == d2;
// 也可以显示调用,不过一般不这样 ,上面的与下面的写法是一样的,相同的意思
operator==(d1, d2);
return 0;
}
But it's still bad, and it's bad behavior to make private variables public in order to use operator overloading.
To do this we need to put the function into the class, but we need to make a little modification because of the existence of the this pointer
If you put it in directly without modification, the compiler will report the following error
After the modification is as follows
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d)
{
return _year == d._year //_year 前面默认有一个 this指针,下面同理
&& _month == d._month
&& _day == d._day;
}
// 我们不写,编译器生成的默认拷贝构造函数
private: //可以被类里面的成员函数访问了
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 9, 22);
Date d2(2023, 1, 1);
cout << (d1 == d2) << endl; // 编译器会转换成 d1.operator == (d2),所以在使用的时候要注意运算符的优先级(这里用括号括起来)
d1 == d2;
// 也可以显示调用,不过一般不这样写 ,上面的与下面的写法是一样的,相同的意思
cout << (d1.operator == (d2)) << endl; //
return 0;
}
To sum up, you must not forget the this pointer. It can be said that any member function (including constructors) contains a hidden this pointer, except for the following static member functions
It was a bit too long, so I wrote it in parts