今天又重新回到c++的学习中~在前两篇博客中,我简单的学习了类的定义,实例化,以及类中的默认成员函数.下篇是类和对象的收尾篇,在这篇中我将补充一下中篇所讲的构造函数以及介绍一些类和对象的新知识.下面让我们开始学习吧.
再谈构造函数
在之前我们实现构造函数时,初始化成员变量主要用函数体内的赋值,但是构造函数还可以用初始化列表进行成员变量的初始化,(并且最好以后都用初始化列表).初始化列表使用方式为以一个冒号开始,以逗号分隔的数据成员列表,每个成员变量后跟一个放在括号中的初始值或表达式(可以写在一行,但推荐每个成员独占一行).
每个成员变量在初始化列表中只能出现一次,初始化列表是每个成员变量定义初始化的地方.
引用成员变量,const修饰的成员变量,没有默认构造的类类型变量必须在初始化列表处进行初始化,否则会编译报错.
c++11支持在成员变量的声明处给缺省值.这个初始值会给到初始化列表,用于给没有在初始化列表中显示初始化的成员初始化.如果在初始化列表中显示初始化了缺省值就没用了.
尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会⽤这个缺省值初始化。如果你没有给缺值,对于没有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显⽰在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序⽆关。(建议类的成员变量的声明顺序和初始化列表顺序保持⼀致).
注:⽆论是否显⽰写初始化列表,每个构造函数都有初始化列表.
⽆论是否在初始化列表显示初始化成员变量,每个成员变量都要⾛初始化列表初始化.
下表为上面内容的大体总结:
下面为一些对应代码:
//简单的实验初始化列表
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Date
{
public:
//错误示范
//Date(int year , int month , int day )
// :_year(2025)
// , _month(3)
// , _day(13)
//{
//
//}
//正确做法
Date(int year=2025, int month=3, int day=13)
:_year(year)
,_month(month)
,_day(day)
{
}
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//错误示范对应的测试
//Date d1;
//会报错,不要将初始画列表与函数的缺省值弄混了
//这里没有缺省值,必须要写实参,如下
//Date d1(2222, 2, 2);
//d1.Print();
//调用之后发现无论给什么值去初始化最后都会打印初始化列表对应的日期
Date d1;
d1.Print();
Date d2(2024,3,14);
d2.Print();
return 0;
}
class Time
{
public:
Time(int hour)
:_hour(hour)
{
cout << "Time(int hour)" << endl;
}
private:
int _hour;
};
class Date
{
public:
Date(int year = 2025, int month = 3, int day = 13)
:_year(year)
, _month(month)
, _day(day)
,_a(_day)
,_b((int*)malloc(sizeof(int)*4))//可以用表达式来初始化
, _t1(10)
{
//错误示范
// 引用成员变量,const修饰的成员变量,没有默认构造的类类型变量
// 必须在初始化列表处进行初始化,否则会编译报错.
//a = _day;
//b = 2;
//t1(1);
}
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
cout << _a << " " << _b << " " << endl;
}
private:
int _year;
int _month;
int _day;
int& _a;
const int* _b;
Time _t1;
};
// class Time
//{
//public:
// Time(int hour)
// :_hour(hour)
// {
// cout << "Time(int hour)" << endl;
// }
//private:
// int _hour;
//};
//class Date
//{
//public:
// Date(int year = 2025, int month = 3, int day = 13)
// :_a(_day)
// , _b((int*)malloc(sizeof(int) * 4))//可以用表达式来初始化
// , _t1(10)
// {
// _year = year;
// _month = month;
// _day = day;
// //也可以混着用
// }
// void Print()
// {
// cout << _year << " " << _month << " " << _day << endl;
// cout << _a << " " << _b << " " << endl;
// }
//private:
// int _year;
// int _month;
// int _day;
//
// int& _a;
// const int* _b;
// Time _t1;
//};
int main()
{
Date d1;
d1.Print();
return 0;
}
class Date
{
public:
Date(int year = 2025, int month = 3, int day = 13)
{
}
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
private:
//可以在声明时给缺省值,作用在初始化列表上
int _year=2024;
int _month=5;
int _day=21;
};
int main()
{
Date d1;
d1.Print();
return 0;
上面这个程序输出见上.因为_a1,_a2在初始化列表上都显示初始化了,所以下面的缺省值就没有用了.又因为初始化列表中按照成员变量在类中声明顺序进行初始化,所以输出值为1,随机值.
类型转换
c++支持内置类型隐式转化为类类型的对象,但需要有相关内置类型为参数的构造函数.
构造函数前⾯加explicit就不再⽀持隐式类型转换.
类类型的对象之间也可以隐式转换,需要相应的构造函数⽀持.
下面为一些代码:
class A
{
public:
//explicit A(int a = 1)
// :_a1(a)
//{
// cout << "A(int a)" << endl;
//}//加了explicit之后就禁止隐式转换了
A(int a = 1)
:_a1(a)
{
cout << "A(int a)" << endl;
}
A(int a, int b)
:_a1(a)
, _a2(b)
{
cout << "A(int a, int b)" << endl;
}
int Geta1()const//因为下面为只读引用,所以这里要加const修饰*this
{
return _a1;
}
private:
int _a1 = 1;
int _a2 = 1;
};
class B
{
public:
B(const A& a)//这里用const修饰了,a就只读了所以*this也要只读(205行)
:_b(a.Geta1())
{
cout << "B(const A& a)" << endl;
}
private:
int _b = 1;
};
int main()
{
A aa1 = 2;//隐式类型转换+拷贝构造
A aa2 = { 3,4 };//隐式类型转换+拷贝构造
B bb = aa1;//隐式类型转换+拷贝构造
return 0;
}
static成员
⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化.静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区.
静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表.静态成员变量第一次创建(在内存中开空间)是在他第一次使用的时候.
⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针.静态成员函数中可以访问其他的静态成员,但是不能访问类中非静态的,因为没有this指针(静态成员函数一般用于取得静态成员变量).但是非静态的成员函数,可以访问任意的静态成员变量和静态成员函数.
突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数.(在全局中可以通过上两种方式来初始化私有的静态成员变量).静态成员也是类的成员,受public、protected、private 访问限定符的限制.(若在类中设置成私有那么就不可以在main函数中突破类域直接访问).
练习:实现⼀个类,计算程序中创建出了多少个类对象?
class A
{
public:
A(int n = 0)
:_aa(n)
{
++_a;
}
//~A()
//{
// --_a;
//}
static int Get_a()
{
return _a;
}
private:
int _aa;
static int _a;
};
int A::_a = 0;
void func()
{
A a4;
}
int main()
{
A a1;
A a2;
A a3;
func();
cout << a1.Get_a() << endl;
cout << A::Get_a() << endl;//也可以通过类来调用静态成员变量
return 0;
}
好啦,今天的学习就到这里啦,在下一篇博客会把类和对象的最后一点东西写完,那我们下篇博客见.晚安~