⭐️今天我要和大家分享C++中类和对象中有关6个默认成员函数的相关知识。
⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/cpp-class-code
在一个类中,会默认生成6个默认成员函数,即使是空类也是如此。
分类如下:
概念: 一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,一般是用来初始化成员变量,且一个生命周期只调用一次。
构造函数特性:
class Date
{
public:
// 构造函数
// 1.无参构造函数
Date()
{
}
// 2. 有参构造
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 3.全缺省
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
构造函数显示定义的三种方法:
类中无构造函数时,编译器会默认生成一个无参的默认构造函数。
默认构造函数有三种:全缺省、不含参和编译器给的
在我们不实现构造函数时,编译器就会调用编译器默认生成的构造函数,看下面一串代码,编译器的默认构造函数能否实现成员变量的初始化呢?
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
3个值都是随机值,编译器的默认构造函数并没有进行对成员变量进行初始化,是不是说这个函数就没有什么用呢?
当然不能这么说,C++中把类型分为内置类型(基础类型)和自定义类型(class,struct等),编译器的默认构造函数对内置类型不处理,对自定义类型会调用它自己的默认构造函数,如果不存在(调有参时),编译器会报错。
看下面的代码,我给大家演示一下:class A { public: A() { cout << "A()" << endl; } private: int _a; }; class Date { public: void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; A _a; }; int main() { Date d1; return 0; }
概念: 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特性:
class Array
{
public:
Array(int length = 0)
{
_length = length;
_p = (int*)malloc(sizeof(int) * _length);
}
// 析构函数
~Array()
{
if (_p)
{
free(_p);
_p = nullptr;
}
}
private:
int _length;
int* _p;
};
如果我们没有实现析构函数,编译器会自动生成一个析构函数,且这个析构函数堆内置类型不处理,对自定义类型会调用它的析构函数,这一点和编译器生成的默认构造函数有点相似。
一个小思考
看下面一串代码,他们的构造和析构顺序是怎么样的?
class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; class B { public: B() { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; } private: int _b; }; int main() { A a; B b; return 0; }
概念: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特性:
class Date
{
public:
// 拷贝构造
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
AA _a;
};
下面解释为什么传值会无穷递归
如果未定义拷贝构造函数,编译器会默认生成一个拷贝构造函数,默认的拷贝构造函数会对内置类型按内存存储的字节序完成拷贝,对于自定义类型编译器会调用他们的默认构造函数,这种拷贝是浅拷贝。
浅拷贝有很大的潜在危险,看下面一串代码:class Array { public: Array(int length = 0) { _length = length; _p = (int*)malloc(sizeof(int) * _length); } // 析构函数 ~Array() { if (_p) { free(_p); _p = nullptr; } } private: int _length; int* _p; }; int main() { Array a1(10); Array a2(a1); return 0; }
概念: 运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数原型:返回值 operator操作符(参数列表)
需要注意的几点:
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// operator==
bool operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 1, 20);
Date d2(2022, 1, 19);
cout << (d1 == d2) << endl;// ==>d1.operator==(d2)
return 0;
}
d1 == d2 <==> d1.operator==(d2)
两种调用方式等价
原型: 返回值 operator=(参数列表)
特性:
Date& operator=(const Date& d)
{
// 检测是否自己给自己赋值
if (this == &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
相同:没有显示定义时,编译器都会默认生成一个,对于内置类型进行字节序的浅拷贝,对自定义类型会调用它自身的拷贝构造函数或operator=。
不同:
拷贝构造:用一个已经存在的对象初始化一个马上要创建的对象。
赋值重载:两个已经存在的对象之间进行赋值。
先看下面一个类:
class Person
{
public:
Person(string name, int age)
{
_name = name;
_age = age;
}
private:
string _name;
int _age;
};
前置++和后置++最大的区别就是返回值不同,前置是返回变化之后的值,后置是返回变化之前的值,两个在重载是,都是operator++,我们如何区分呢?
一般operator++默认是前置++,为了区分后置++,我们通常会在参数列表加一个占位参数,且这个参数必须是int类型的,从而构造成函数重载。实现如下:class Person { public: Person(string name, int age) { _name = name; _age = age; } // 前置++ Person& operator++() { _age++; return *this;// 返回变化之后的值,传引用 } // 后置++ Person operator++(int) { Person ret = *this; _age++; return ret;// 返回变化之前的值,传值 } void Print() { cout << _name << "-" << _age << endl; } private: string _name; int _age; }; int main() { Person p("wxj", 19); p.Print(); cout << "前置++" << endl; // 前置++ Person ret = ++p; ret.Print(); p.Print(); cout << "后置++" << endl; // 后置++ ret = p++; ret.Print(); p.Print(); return 0; }
概念: 将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
一般我们再成员函数后面加const,就会使得this的类型变为 const Date* const ,也就是该成员函数中成员变量不能够被修改。
事例:
bool operator==(const Date& d) const // const Date* const this
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
这两个函数一般自己不定义,都是编译器自己生成。
Date* operator&()
{
return this ;
}
const Date* operator&() const
{
return this ;
}
类和对象的大体内容就差不多是这些了,类和对象(下)还会继续介绍一些有关的零碎知识。喜欢的话,欢迎大家点赞支持~