自助 建站,怎样做网站排名优化,做捕鱼网站电话,圣辉友联网站建设文章目录 1.类的6个默认成员函数2.构造函数2.1基本概念和用法2.2初始化列表2.3explicit关键字 3.拷贝构造函数4.重载赋值运算符 1.类的6个默认成员函数
如果定义一个空类#xff0c;其实并不是什么都没有#xff0c;编译器会默认生成6个默认的成员函数#xff01;默认成员函… 文章目录 1.类的6个默认成员函数2.构造函数2.1基本概念和用法2.2初始化列表2.3explicit关键字 3.拷贝构造函数4.重载赋值运算符 1.类的6个默认成员函数
如果定义一个空类其实并不是什么都没有编译器会默认生成6个默认的成员函数默认成员函数如果用户不显式实现编译器会生成的成员函数称为默认成员函数
class Date {};2.构造函数
2.1基本概念和用法
构造函数名字与类名相同无法返回值的特殊成员函数创建类类型对象时由编译器自动调用保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次构造函数名字听着像是构造对象的但是构造函数的主要任务并不是开空间创建对象而是初始化对象。
class student
{
public:student() { cout _age value is _age endl;_age 19;cout student()构造函数被调用 endl; }
private:int _age 18; // 不是初始化注意是_age声明
};int main()
{// 对象自定义类型变量示例化在栈上开辟空间student stu;return 0;
}注意
构造函数可以被重载拷贝构造函数和构造函数构成重载如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。编译器默认生成的构造含C标准规定对内置类型如整形int double 、任何类型的指针等不做处理、对自定义类型会调用它的默认构造函数默认构造函数包括无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数C标准是规定了不做处理但是标准不是人人都遵守的VS下有些版本会处理太狠了C11提供了缺省参数的方式声明是做了处理当调用构造函数时会根据缺省值初始化如int _age 18;
class student
{
public:// 注意这里显示写初始化列表就不去走 int _age 18;这里的缺省了太坑了student() :_age(){ cout _age value is _age endl;}int _age 18;
};
int main()
{student stu;return 0;
}class Date {
private:int _year;int _month;int _day;public:Date(){_year 10; _month 10; _day 10;}Date(int year 5, int month 5, int day 5){_year year;_month month; _day day;}
};
int main()
{//这里无法通过编译有两个无参构造函数产生歧义。构成重载但是产生歧义Date date;return 0;
}2.2初始化列表
class student
{
public:student() { _age 19;cout student()构造函数被调用 endl; }
private:int _age 18; // 不是初始化注意是_age声明
};这里使用缺省参数int _age 18;是声明,在student() {_age 19;}构造函数中也不能算是真正的初始化构造函数体中的语句只能将其称为赋初值因为初始化只能初始化一次而构造函数体内可以多次赋值。
真正初始化的地方在初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个**成员变量后面跟一个放在括号会去调用别人拷贝构造中的初始值或表达式**。
class student
{
public:student(int age 18):_age(age){ cout _age value is _age endl;}
private:int _age;
};
int main()
{student stu1;student stu2(19);return 0;
}注意每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)尽量优先是初始化列表初始化对于自定义类型成员变量一定会先使用初始化列表初始化。 类中包含以下成员必须放在初始化列表位置进行初始化1.const成员变量 2.引用成员变量 3.自定义类型成员(且该类没有默认构造函数时)
class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:B(int a, int ref):_aobj(a), _ref(ref), _n(10){}
private:A _aobj; // 没有默认构造函数int _ref; // 引用const int _n; // const
};int main()
{B b(1, 2);
}成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() { cout _a1 _a2 endl; }
private:int _a2;int _a1;
};
int main()
{A aa(1);aa.Print();
}输出结果
1 -8589934602.3explicit关键字
构造函数不仅可以构造与初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数还具有类型转换的作用。
class Date
{
public:Date(int year, int month 1, int day 1):_year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};int main()
{Date d1 2021;return 0;
}Date d1 2021;隐式类型转换调用Date(int year, int month 1, int day 1)构造函数。
class Date
{
public:explicit Date(int year, int month 1, int day 1):_year(year), _month(month), _day(day){}...
};
int main()
{Date d1 2021;return 0;
}在构造函数前加上explicit修饰不能隐式转换当然在字符串中隐式类型转换还是很好的
#includeiostream
#includestring
using namespace std;int main()
{// 1.隐式类型转换string str1 hello;// 2.调用构造函数,和上面的等价string str2(hello);return 0;
}3.拷贝构造函数
拷贝构造函数是构造函数的一个重载形式拷贝构造函数的参数显示的有一个隐藏的this只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用。
int fun(int num)
{cout int fun(int num){} endl;return num;
}上面的的int fun(int num)函数传参和返回值传值返回会发生两次拷贝如果我们**显示实现自定义类的拷贝构造函数Date(const Date date)**会更直观。
class Date
{
public:Date(int year2023, int month 1, int day 1):_year(year), _month(month), _day(day){}Date(const Date date){_year date._year;_month date._month;_day date._day;}int _year;int _month;int _day;
};Date fun(Date date)
{cout Date fun(Date date){} endl;return date;
}int main()
{Date d1;fun(d1);
}通过调试fun(d1);发现会调用两次拷贝构造函数Date fun(Date date)一次是传参一次是返回这所以拷贝构造函数不能使用传值的方式Date(Date date)方式在拷贝构造中重复调用拷贝构造引发无穷递归但是不用担心编译器会制止这样的行为
对Date fun(Date date)做修改
Date fun(const Date date)
{cout Date fun(Date date){} endl;return date;
}引用做参数不会调用拷贝构造函数提高效率当然在函数体内部修改对象出了函数作用域修改的值也存在但是上面加上了const说明date对象不能修改但外部的const和非const对象都能传递。
4.重载赋值运算符
class Date
{
public:Date(int year2023, int month 1, int day 1):_year(year), _month(month), _day(day){}Date(const Date date){_year date._year;_month date._month;_day date._day;}Date operator (const Date date){if (this ! date){_year date._year;_month date._month;_day date._day;}return *this;}void print(){std::cout _year - _month - _day std::endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 1);Date d2 d1;Date d3;d3 d1;d2.print();d3.print();
}上面是正确的写法另外注意Date d2 d1;这里连续的构造和赋值拷贝我在VS2022编译器下优化成了一次拷贝构造。 为什么Date operator (const Date date)的返回值是Date那void和Date能不能行 class
{...void operator (const Date date){if (this ! date){_year date._year;_month date._month;_day date._day;}}...
}int main()
{Date d1(2024, 2, 1);Date d2;Date d3;d2 d1;d3 d2 d1;return 0;
}这行d3 d2 d1;是用void返回值不能连续的赋值那使用Date会怎么样
class
{...Date operator (const Date date){if (this ! date){_year date._year;_month date._month;_day date._day;}return *this;}...
}int main()
{Date d1(2024, 2, 1);Date d2(2021, 2, 1);Date d3;(d3 d2) d1;d2.print();d3.print();return 0;
}输出结果
2021-2-1
2021-2-1结果不是我想要的啊此时由于括号()改变了结合顺序先执行d3d2,结果是将d2的值赋值给d3这里是没有问题的其实d3 d2 等价于 d3.operator(d2)函数返回值如果是非引用返回的右值引用返回的是左值这里Date operator (const Date date)非引用返回右值但是右值充当左值出现异常