1. 再谈构造函数
2.static成员
3.友元
4.内部类
5.匿名对象
1. 再谈构造函数
1.1构造函数体内赋值
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
int main()
{return 0;
}
构造函数体种的语句只能将其称为赋值,不能叫做初始化。
它是不能够做到给const数据成员初始化的。
1.2初始化列表
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,没个数据成员后面有一个小括号,括号里面写用于初始化的初始值或表达式。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _year(year)//初始值, _month(month), _day(day),arr((int*)malloc(sizeof(int)))//表达式{}
private:int _year;int _month;const int _day;int* arr;
};
int main()
{return 0;
}
1.每个数据成员再1初始化列表中只能出现一次(初始化只能进行一次)
2.类中包含以下成员,必须在初始化列表初始化:
引用成员变量
const成员变量
自定义类型成员(且没有默认构造函数)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
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 d(1, 2);return 0;
}
3.尽量使用初始化列表初始化。
4.成员变量在类中的声明次序就是初始化顺序,初始化与初始化列表中的先后次序无关
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
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();return 0;
}
因为_a2是先声明的,所以先对_a2初始化,由于此时_a1还没被初始化,所以_a1是随机值,所以_a2被初始化成了随机值。
5.缺省值与初始化列表的联系
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class A
{
public:void Print() {cout << _a1 << " " << _a2 << endl;}
private:const int _a2 = 1;const int _a1 = 2;
};
int main() {A aa;aa.Print();return 0;
}
上面说过,const数据成员一定要在初始化列表初始化,那这里没写,为什么是对的呢?难道是在声明时初始化吗?
解答:
1.编译器默认生成了初始化列表。
2.没有声明时初始化这个说法,这种写法是给缺省值,如果不为数据成员指定初始化的内容,那么将使用这个缺省值。所以,编译器是将这个缺省值给到初始化列表,然后再由初始化列表对数据成员初始化。
补充:缺省值也是可以给表达式的。
1.3类型转换
内置类型的变量可以通过类型转换变为自定义类型的对象。
而类型转换是通过构造函数实现的。
1.3.1给构造函数传1个参数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:int _a;Date(int a):_a(a){}
};
int main()
{Date d = 3;cout << d._a;return 0;
}
1.3.2给构造函数传多个参数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:int _a;int _b;Date(int a, int b):_a(a), _b(b){}
};
int main()
{Date d = { 3,5 };cout << d._a << endl << d._b;return 0;
}
1.4explicit关键字
explicit修饰构造函数,禁止类型转换
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:int _a;int _b;explicit Date(int a, int b):_a(a), _b(b){}
};
int main()
{Date d = { 3,5 };//无法类型转换,报错cout << d._a << endl << d._b;return 0;
}
2.static成员
2.1概念
用static修饰成员变量,称为静态成员变量; 用static修饰成员函数,称为静态成员函数。
2.2特性
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
2.静态成员变量必须在类外定义,定义不加static,要使用作用域运算符说明该静态成员变量来自哪个类。注意:类中的只是声明,且静态成员只能在全局定义。
3.类静态成员可用类名::静态成员或者对象.静态成员访问。
4.静态成员函数没有this指针,不能访问非静态成员(包括成员函数,调用成员函数也需要this指针)。
5.静态成员也是类的成员,受public,protect,private限制。(定义时无限制)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:static int a;static void test(){//cout << c;报错,不能访问非静态成员//test1();报错,非静态成员函数也不能cout << b;//静态成员可以test2();}static void test2() {};void test1() {};
private:static int b;int c = 1;
};
int Date::a = 5;//类外定义,不加static,用::说明a来源于Date类
int Date::b = 1;//只能在全局初始化
int main()
{Date d;cout << Date::a << endl;//类名::静态成员访问cout << d.a << endl;//对象.静态成员访问//cout << Date::b;报错,受public限制return 0;
}
3.友元
友元函数提供了一种突破封装的方式,提供了便利,但同时破坏了封装。(不宜多用)
友元分为:友元函数和友元类
3.1友元函数
3.1.1概念
友元函数时定义在类外的普通函数,不属于任何类,但需要在类内友元声明(和之前的声明一样写,最后在声明开头加上friend)。
3.1.2特性
1.友元函数可以访问类的私有和保护成员
2.一个函数可以是多个类的友元函数
3.友元函数可以在类内任何位置声明,不受private,protect,public限制
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date;//在A类中对test函数友元声明要用到Date,此时Date还为声明//这种写法不算重定义,这么写只是向编译器说明该源文件中有//Date类,和函数的声明一样。
class A
{friend void test(const Date& d, const A& t);
private:int a = 2;
};
class Date
{friend void test(const Date& d, const A& t);
private:int a = 1;};
void test(const Date& d, const A& t)
{cout << d.a << endl;//访问私有成员cout << t.a << endl;
}
int main()
{Date d;A t;test(d, t);return 0;
}
注意:编译器通过花括号的有无来判断是声明还是定义,若有花括号,则是定义;若无花括号,则是声明。
3.2友元类
3.2.1概念
将一个类在另一个类中友元声明(和之前的声明一样写,最后在声明开头加上friend),那么该类称为另一个类的友元类,该类中的所有成员函数都是另一个类的友元函数。
3.2.2特性
1.友元是单向的
如果A是B的友元,不能说明B是A的友元,此时,A能访问B的私有,但B不能访问A的私有
2.友元关系不能传递
如果C是B的友元,B是A的友元,不能说明C是A的友元
3.友元关系不能继承(以后讲)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class A
{friend class Date;//友元声明int b = 1;int a = 2;
};
class Date
{int a = 1;
public:void test(const A& a){cout << a.a << endl << a.b << endl;//访问A类的私有成员}
};
int main()
{Date d;A a;d.test(a);return 0;
}
4.内部类
4.1概念
如果一个类的定义在另一个类的内部,那么该类就叫内部类。内部类是一个独立的类,不属于任何外部类。
4.2特性
1.内部类可以通过对应外部类的对象访问私有成员。注意:本质上,内部类就是外部类的友元类。但是,外部类不是内部类的友元类。
2.内部类可以直接访问对应外部类的静态成员(不需要通过类名或对象访问,通过类名或对象访问也是对的)
3.对外部类计算大小时,不计入内部类的部分(内部类是一个独立的类,不属于任何外部类。)
4.不能通过外部类的对象直接访问内部类的成员;内部类也不能直接访问外部类的非静态成员(内部类是一个独立的类,不属于任何外部类。)
注意:可以用内部类的对象间接访问
5.内部类受外部类的类域限制(类域类似于命名空间域)
6.内部类受private等访问限定符的限制
总结:友元性,静态直接访问性,受限制性,独立性(作者自己编的名字,无权威)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
private:class A1{};int a = 1;static int b;
public:class A2{private:int e = 4;public:int d = 3;void test(const Date& d){//cout << a;报错,不能直接访问外部类的非静态成员cout << b << endl;//可以直接访问外部类的静态成员cout << Date::b << endl;//间接访问也对cout << d.b << endl;cout << d.a << endl;//可以间接访问外部类的非静态成员//内部类是外部类的友元类,可以访问静态成员}};void test(const A2& a){//cout << d; 报错,不能直接访问内部类cout << a.d << endl;//可以间接访问//cout << a.e; 报错,外部类不是内部类的友元}
};
int Date::b = 2;
int main()
{//A2 a; 报错,内部类受类域限制//Date::A1 a1; 报错,内部类受private限制Date::A2 a2;Date d;a2.test(d);d.test(a2);return 0;
}
5.匿名对象
5.1概念
顾名思义,匿名对象就是没有名字的对象
5.2特性
1.生命周期只在当前语句
2.定义方法:类名(参数);
注意:这里的参数是传给构造函数的
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:int _a = 2;Date(){cout << "Date()" << endl;}Date(int a):_a(a){cout << "Date(int a)" << endl;}~Date(){cout << "~Date()" << endl;}
};
int main()
{Date(1);cout << "11111111111" << endl;Date();return 0;
}
3.如果不给构造函数传参,括号必须写!
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:int _a = 2;Date(){cout << "Date()" << endl;}Date(int a):_a(a){cout << "Date(int a)" << endl;}~Date(){cout << "~Date()" << endl;}
};
int main()
{Date;//这里虽然没报错,但是对象没有创建return 0;
}