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;
}