C++:类和对象(3)|初始化列表|类型转换
上篇文章:https://blog.csdn.net/2401_86123468/article/details/152418434?spm=1001.2014.3001.5501
1.构造函数进阶(初始化列表)
1.之前我们实现构造函数时,初始化成员变量主要使用函数体赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式。
函数体赋值:
#include<iostream>
using namespace std;class Date
{
public:Date(int& x, int year = 1, int month = 1, int day = 1){// 函数体内为普通成员赋值_year = year;_month = month;_day = day;_t = 12;}
private:int _year;int _month;int _day;int _t;
};
初始化列表:
class Date
{
public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){//函数体赋值}
private:int _year;int _month;int _day;
};
2.每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
3.引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};class Date
{
public:
//危险的引用:int x = 1,Date(int& x, int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day)//以上三个在函数体赋值也可以, _t(12), _ref(x)//引用外部的变量, _n(1)//const在这初始化没问题{//不能在函数体内对引用成员变量,const成员变量,没有默认构造的类类型变量初始化// error C2512: “Time”: 没有合适的默认构造函数可用// error C2530 : “Date::_ref” : 必须初始化引用// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}private:// 声明int _year;int _month;int _day;int& _ref; // 引用const int _n; // constTime _t; // 没有默认构造,需要自己完成
};int main()
{int i = 0;//初始化引用//对象整体定义Date d1(i);d1.Print();return 0;
}
在之前章节,MyQueue里面的两个栈会自动调用两个栈的默认构造:
class Stack
{
public:Stack(){cout << "Stack()" << endl;}
};class MyQueue
{
public:private:Stack _st1;Stack _st2;
};int main()
{MyQueue q1;return 0;
}
如果它没有默认构造,就需要自己通过初始化列表进行初始化:
class Stack
{
public:Stack(int n){cout << "Stack(int n)" << endl;}
};class MyQueue
{
public:MyQueue(int n = 4):_st1(n),_st2(n){ }
private:Stack _st1;Stack _st2;
};int main()
{MyQueue q1;return 0;
}
4.C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};class Date
{
public:private:// 声明,缺省值int _year = 1;int _month = 1;int _day = 1;
};int main()
{Date d1;return 0;
}
如果有显示初始化,则优先访问初始化中的值:
缺省值的使用场景(定义size):
class Stack
{
public:Stack(int n = 4){cout << "Stack(int n)" << endl;}
};class MyQueue
{
public:
private:int _size = 0;Stack _st1;Stack _st2;
};
int main()
{MyQueue q1;return 0;
}
5.尽量使用初始化列表初始化,因为那些不在初始化列表初始化的成员也会走初始化列表(因为初始化列表是每个成员变量定义初始化的地方),如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
建议尽量使用初始化列表显示初始化,如果没有,就尽量给缺省值,更深层次的给初始化,检查等,需要在函数体内进行:
class A
{
public:A(int n = 10):_a((int*)malloc(sizeof(int)*n)),_size(0){// 检查,只能在函数体内进行if (_a == nullptr){cout << "malloc fail" << endl;exit(-1);}//赋值……memset(_a, 0, sizeof(int) * n);}private:int* _a;int _size;
};int main()
{A aa;return 0;
}
6.初始化列表中按照成员变量在类中声明顺序进行初始化,与在初始化列表出现的先后顺序无关,建议声明顺序和初始化列表顺序保持一致。
#include<iostream>
using namespace std;
class A
{
public:A(int a):_a1(a), _a2(_a1){ }void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2 = 2;int _a1 = 2;
};
int main()
{A aa(1);aa.Print();
}
上述代码,由于成员变量在类中的声明顺序为a2,a1,那么先走初始化列表中的_a2(_a1),此时_a1未定义,为随机值,其次走_a1(a),输出1。
所以最终结果为:输出1,随机值
初始化列表总结:
无论是否显示写初始化列表,每个构造函数都有初始化列表;
无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化;
2.类型转换
int main()
{int i = 1;double d = i;//隐式类型转换,中途产生临时变量//double& ref = i;//此时编译不通过,临时变量具有常性,此时写法类似权限放大const double& ref = i;//此时编译通过return 0;
}
1.C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
有一定关联才能转换:
<1>内置类型与内置类型之间的转换,整形家族(整形和浮点,整形和指针),浮点数和指针之间不能转换,没有关联关系。
<2>C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
<3>类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
class A
{
public:A(int a1):_a1(a1){}
private:int _a1 = 1;int _a2 = 2;
};
int main()
{//构造A a1(1);//A(int a1)//隐式类型转换A a2 = 1;const A& ref1 = a1;const A& ref2 = 1;return 0;
}
具体举例:
class A
{
public:A(int a1):_a1(a1){ }
private:int _a1 = 1;int _a2 = 2;
};//void func(A aa)//不考虑传值传参,拷贝构造是种浪费
//{ }
void func(const A& aa1)//传引用传参
{}class Stack
{
public:void Push(const A& a){ }
};int main()
{//构造A a1(1);//A(int a1)//隐式类型转换A a2 = 1;const A& ref1 = a1;const A& ref2 = 1;func(a1);func(1);//如果func函数不加const,这里会编译错误//对Stack类,不使用类型转换的写法:Stack st1;A a3(3);st1.Push(a3);//使用类型转换:Stack st2;st2.Push(3);//用int去构造A,创建临时变量return 0;
}
上述的func函数,也可以直接给缺省参数:
class A
{
public:A(int a1):_a1(a1){}void print() const {cout << "_a1 = " << _a1 << ", _a2 = " << _a2 << endl;}private:int _a1 = 1;int _a2 = 2;
};
void func(const A& aa2 = 1)//也可以通过缺省参数
{cout << "func中的对象:";aa2.print();
}
int main()
{A a1(3);cout << "a1对象:";a1.print(); // 输出a1的成员值func(a1);cout << "传递4给func:";func(4);cout << "不传参调用func(默认1):";func();//可使用缺省参数return 0;
}
2.构造函数前面加explicit就不再支持隐式类型转换。
同样的情况下,通过Linux系统,关闭g++的优化
多参数下:
3.类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
本章完。