网站建设费用大概多少钱,衡阳市确诊名单,农业科技工作服务站建站模板,找人做网站注意什么问题目录 一. 初始化列表
1.1 对象实例化时成员变量的创建及初始化
1.2 初始化列表
1.3 使用初始化列表和在函数体内初始化成员变量的效率比较
1.4 成员变量的初始化顺序
1.5 explicit关键字
二. static成员
2.1 static属性的成员变量
2.2 static属性的成员函数
三. 友元 …目录 一. 初始化列表
1.1 对象实例化时成员变量的创建及初始化
1.2 初始化列表
1.3 使用初始化列表和在函数体内初始化成员变量的效率比较
1.4 成员变量的初始化顺序
1.5 explicit关键字
二. static成员
2.1 static属性的成员变量
2.2 static属性的成员函数
三. 友元
3.1 友元函数
3.2 友元类 一. 初始化列表
1.1 对象实例化时成员变量的创建及初始化
以演示代码1.1中的日期类Date为例Date类中有一个用户显示定义的构造函数来实现在类实例化时对成员变量赋初值。但是成员变量并不是在构造函数的函数体内部进行创建的在进入构造函数的函数体内部之前成员变量会先被给一个随机的值然后到构造函数内部改变这个随机的值来让成员变量初始化为用户希望的值。
因此我们可以认为Date成员变量是先被创建出来不进行初始化然后在被赋初值的。
演示代码1.1
class Date
{
public:Date(int year, int month, int day){_year year; //进入函数体内部时成员变量已被创建_month month;_day day;}
private:int _year;int _month;int _day;
};
1.2 初始化列表
先将成员变量创建出来再初始化相比于创建时直接初始化效率相对较低而且对于引用、具有const属性以及没有默认构造函数的自定义类型成员变量其必须在定义创建的同时完成初始化。因此在函数体内部初始化成员变量就有其局限性。
为了实现在定义创建成员变量的同时进行初始化C引入了初始化列表。其语法格式为以一个冒号开始冒号在函数声明的后面以逗号分隔成员变量每个成员变量后面跟一个括号表示被初始化的值。
演示代码1.2通过初始化列表实现了对日期类三个成员变量的初始化。
演示代码1.2
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;
};
注意下面三种类型的成员变量只能采用初始化列表进行初始化
引用类型成员变量。具有const属性的成员变量。没有默认构造函数的自定义类型成员变量。
演示代码1.2三种只能使用初始化列表的成员变量的初始化
class A
{
public:A(int a):_a(a){}private:int _a;
};class B
{
public:B(A a, int ref)//没有默认成员函数的内置类型、引用以及const属性成员变量//只能用初始化列表初始化: _a1(a), _ref(ref), _n(10){}private:A _a1; //没有默认成员函数的内置类型int _ref; //引用const int _n; //const属性成员变量
};
1.3 使用初始化列表和在函数体内初始化成员变量的效率比较
演示代码1.3创建一个含有默认构造函数的类AA在类A中包含类型为AA的自定义成员变量先后显示定义两种不同形式的默认构造函数一是在函数体内部初始化自定义成员变量二是在初始化列表初始化自定义类型成员变量。
为了方便观察两种情况下构造函数、拷贝构造函数和赋值运算符重载函数分别调用了几次函数应输出相关信息。运行代码可以看到如果在函数体内部初始化需要调用两次调用构造函数、一次调用赋值运算符重载函数如果使用初始化列表初始化仅需要调用构造函数和拷贝构造函数各一次。对于较大的自定义类型成员变量使用初始化列表可以显著提高程序运行效率。
这是因为使用初始化列表在成员变量被定义出来的同时完成了初始化而在函数体内部初始化则将成员变量的定义和初始化拆分为两个步骤。
演示代码1.3
class AA
{
public:AA(int a 1, int b 2) //构造函数{cout AA(int a, int b) endl;_a a;_b b;}AA(const AA aa) //拷贝构造函数{cout AA(AA aa) endl;_a aa._a;_b aa._b;}AA operator(const AA aa) //赋值运算符重载函数{cout AA operator(const AA aa) endl;_a aa._a;_b aa._b;return *this;}private:int _a;int _b;
};class A
{
public://函数体内初始化//A(int c, int d, const AA aa) //这里会调用初始化列表创建_aa但给随机初值//{// _c c;// _d d;// _aa aa;//}//初始化列表初始化A(int c, int d, const AA aa): _c(c), _d(d), _aa(aa){}private:int _c;int _d;AA _aa;
};int main()
{AA aa1;A a1(10, 20, aa1);return 0;
} 图1.1 在函数体内初始化左和采用初始化列表初始化右时程序运行结果总结对于有默认构造函数的自定义类型成员变量可以在函数体内部初始化也可以使用初始化列表初始化但为了提高程序运行的效率一般建议采用初始化列表初始化。
1.4 成员变量的初始化顺序
成员变量初始化的顺序与声明顺序一致与初始化列表的顺序无关。如演示代码1.4所示虽然初始化列表中_a1在前_a2在后但是_a2依然先被初始化_a1初始化后的值并没有被赋给_a2。因此程序的运行结果并不是1 1而是1 随机值 。
建议成员变量的声明顺序与初始化列表的顺序保持一致。
演示代码1.4
class AA
{
public:AA(int a1 10, int a2 20):_a1(1), _a2(_a1) //按照声明顺序先初始化_a2再初始化_a1{}void Print(){cout _a1 _a1 endl;cout _a2 _a2 endl;}private:int _a2;int _a1;
};int main()
{AA aa(1,1);aa.Print();return 0;
} 图1.2 演示代码1.4的运行结果1.5 explicit关键字
如果一个构造函数只有一个参数或者除了第一个参数外每个参数都有默认的缺省值那么就可以通过隐式类型转换来实现对象的实例化。
演示代码1.5定义了一个类Year通过Year y1 2023实现通过隐式类型转换完成对象实例化。但是某些时候我们不希望这种隐式类型转换的发生C为此提供了一个关键字explicit在函数声明之前添加explicit即可禁止这种隐式类型转换。
演示代码1.5
class Year
{
public://函数前加explicit禁止隐式类型转换explicit Year(int year 2023);void Print();private:int _year;
};Year::Year(int year) //构造函数: _year(year)
{cout Year(int year 2023) endl;
}void Year::Print()
{cout _year endl;
}int main()
{Year y1(2020);//Year y2 2023; //存在隐式类型转换y1.Print();//y2.Print();return 0;
}
二. static成员
2.1 static属性的成员变量
静态(static)成员变量属于整个类并非单独属于某个类对象而是所有类对象共享。必须在类外部定义static成员变量和给定初始化值在类内部只是声明static成员变量。
通过static成员变量可以实现统计一个类被实例化的次数。如演示代码2.1所示在类A中定义了一个static成员变量_s_count每当构造函数或拷贝构造函数被调用时执行_s_count操作表示创建了一个类对象。运行演示代码2.1_s_count的值为3表示进行了3次对象实例化调用构造函数创建对象a1第一次、调用构造函数创建对象a2第二次、调用f()函数传参时拷贝构造形参第三次。
注意static属性的成员变量存储在静态区在使用sizeof计算类或类对象大小时static成员不包含在其中。
演示代码2.1统计类实例化次数
//统计类对象总共被创建的次数
//构造函数和拷贝构造函数都会创建类对象
class A
{
public:A(int a 5) //构造函数: _a(a){cout A(int a 5) endl;_s_count;}A(A a) //拷贝构造函数: _a(a._a){cout A(A a) endl;_s_count;}//非静态成员函数int GetSumCount(){return _s_count;}private:int _a;static int _s_count;
};int A::_s_count 0; //初始化静态成员变量void f(A a)
{ }int main()
{A a1;A a2 1;f(a1);//通过对象调用函数获取_s_count的值cout _s_count a1.GetSumCount() endl; return 0;
} 图2.1 演示代码2.1的运行结果2.2 static属性的成员函数
static属性的成员函数严格来说并不能算是类的成员函数其没有this指针不能访问非static属性的成员变量或调用非static属性的成员函数。
静态成员函数有两种访问方式1对象名.函数名(参数列表)、2类名::函数名(参数列表)
static成员也受访问限定符的限制不能在类外部访问private和protect属性的static成员。虽然静态成员函数不能调用非静态成员函数但是非静态成员函数可以调用静态成员函数。
class A
{
public:static void GetNum(); //静态成员函数private:int _a1 10;static int _a2;
};int A::_a2 20; void A::GetNum()
{//cout _a1 endl; //_a1为非静态成员变量静态成员函数不能访问cout _a2 _a2 endl;
}int main()
{A a;A::GetNum(); //通过类名调用static属性函数A().GetNum(); //通过对象调用static属性函数A()为匿名对象return 0;
}
三. 友元
友元可以分为友元函数和友元类。有一点要事先声明友元是一种严重破坏封装的行为一般建议友元要少用慎用。
3.1 友元函数
对于一个类中的private或protect属性成员全局函数不能访问它们但是如果我们将某个全局函数声明为类的友元函数那么这个全局函数就可以不受访问限定符的约束随意访问类中的成员变量和成员函数。
友元函数的声明形式friend 返回类型 函数名(参数列表)
演示代码3.1展示了友元函数的定义和使用在类A中声明Print为A的友元则全局函数Print可以访问A的私有属性成员变量并输出其值。这里还有一点需要注意友元函数并不是某个类的成员函数它是属于全局的。
演示代码3.1
class A
{friend void Print(A a); //声明Print为A的友元
public:A(int a1 10, int a2 20) //构造函数: _a1(a1), _a2(a2){}private:int _a1;int _a2;
};void Print(A a)
{cout _a1 a._a1 endl;cout _a2 a._a2 endl;
}int main()
{A a;Print(a); //Print为类A的友元return 0;
} 图3.1 演示代码3.1的运行结果3.2 友元类
如果定义一个类A为类B的友元那么这个类B中的任意一个成员函数就可以访问类A中的private或public属性成员。需要注意的是友元类没有传递性和交互性即
如果B是A的友元C又是B的友元不能认为C就是A的友元。假设B是A的友元B可以访问A的非公有属性成员我们也不能认为A是B的友元即一般不能在类A中访问类B的非公有属性成员。
友元类的声明方式friend class 类名
演示代码3.2定义了一个日期类Date和一个时间类Time声明Date类是Time类的友元那么我们就可以在Date类中定义一个SetTimeOfDay函数来设置Date类的Time类型成员变量。进入调试界面打开监视窗口看到Time类型成员_t的每个成员变量的值都依据调用SetTimeOfDay时传递的参数改为了12。
演示代码3.2
class Time
{friend class Date; //友元类声明public:Time(int hour 1, int minute 1, int second 1): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year 2023, int month 3, int day 2) //构造函数: _year(year), _month(month), _day(day){}//Date类的成员函数实现对Time类非公有属性成员的访问void SetTimeOfDay(int hour, int minute, int second){_t._hour hour;_t._minute minute;_t._second second;}private:int _year;int _month;int _day;Time _t;
};int main()
{Date d; //使用默认值完成类实例化d.SetTimeOfDay(12, 12, 12); //时间设为12时12分12秒return 0;
} 图3.2 演示代码3.2