文章目录
- 一、 explicit关键字
- 二、static成员
- 三、友元
- 四、内部类
- 五、匿名对象
一、 explicit关键字
1、隐式类型转换
再进行隐式类型转换是会产生一个临时变量tmp,再用临时变量进行赋值。
如:
double d = 1.2;
//再用 d 给 i 进行赋值时,会进行隐式类型转换。
//过程:产生 tmp = 1 ,再用 tmp 给 i 进行赋值
int i = d;
那么对于类来说是否也能进行这样的隐式类型转换呢?
答:是的,比如 int
转为 类 , 先用 int i
进行转换为 类类型 A
(这个过程会调用构造函数进行构造出一个临时类(tmp)
),再用 tmp
拷贝到 A
即完成了隐式类型转换。
如:
class A
{
public:
//有参构造
A(int a)
:_a(a)
{
cout << "A(int a)" << endl;
}
//拷贝构造
A(const A & a)
{
cout << "A(const A & a)" << endl;
}
private:
int _a;
};
int main()
{
A aa = 3;
//等价与 A tmp(3) , A aa = tmp;
//先将 3(int) 隐式转换为 自定义类型 A (tmp),再aa = tmp (拷贝构造)
return 0;
}
上面演示的是单参数的隐式转换,下面是多参数的隐式转换
//多参数隐式类型转换
class A
{
public:
//有参构造 多参数
A(int a,int b)
:_a(a)
,_b(b)
{
cout << "A(int a)" << endl;
}
private:
int _a;
int _b;
};
int main()
{
//调用格式 ,也可 aa{1,2},但是不建议这种
A aa = { 1,2 };
return 0;
}
补充:
临时变量具有常量性。
int main()
{
double d = 1.12;
//int i = d; 正常进行隐式转换
//int& i = d; //引用临时变量 过程 const int tmp -> int &i = tmp 但是tmp是常量所以会报错
//正确做法 //临时变量如果被引用生命周期会延长,不然用完就销毁了
const int& i = d;
return 0;
}
2、如果不想要编译器进行隐式类型转换呢?
使用 explicit
关键字 就可以了
如:
explicit A(int a,int b)
:_a(a)
,_b(b)
{
cout << "A(int a)" << endl;
}
二、static成员
1、概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
class A
{
public:
//静态成员函数
static void Print()
{
cout << _a << endl;
}
private:
//静态成员变量
static int _a;
};
//在类外定义
int A::_a = 10;
2、static成员的特征
(1). 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
(2). 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
(3). 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
(4). 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
(5). 静态成员也是类的成员,受public、protected、private 访问限定符的限制
class A
{
public:
//静态成员函数
static void Print()
{
cout << _a << endl;
//静态函数不能访问非静态成员
//cout << _b << endl;
}
//静态成员变量 声明
static int _a;
int _b = 10;
};
//在类外定义
int A::_a = 10;
int main()
{
A a1;
cout << sizeof(a1) << endl; // 大小为 4 ,_a 存在静态区
A a2;
cout << "直接通过类来访问" << A::_a << endl;
cout << "a1:" << a1._a << endl; // a1._a == a2._a 这个成员变量共享
cout << "a2:" << a2._a << endl; //当前在公共区域,当设置为私有时对象就不可访问了
return 0;
}
3、静态和非静态的区别
1、调用方式:
非静态成员函数:需要通过类的实例来调用。
静态成员函数: 可以通过类名直接调用,也可以通过实例调用。
2. 访问成员:
非静态成员函数: 可以访问类的实例中的非静态成员变量和其他成员函数。
静态成员函数: 不能直接访问类的实例成员,只能访问类的静态成员变量和其他静态成员函数。
3. this指针:
非静态成员函数: 隐含地有一个指向当前对象的指针(this指针)。
静态成员函数: 没有this指针,因为它与特定的实例无关。
三、友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
关键字:friend
1、友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend
关键字。
(1)如:重载 <<
操作符
我们知道 重载 << 操作符时,不能定义为成员函数,所以只能使用全局函数了,但是类的成员变如果是私有的,对象就无法访问成员变量了,这时使用友元函数就可以很好解决这个问题。
class A
{
//声明友元函数
friend ostream& operator<<(ostream& _cout, A& a);
public:
private:
int _a = 10;
};
ostream& operator<<(ostream& _cout, A &a)
{
//作为友元函数来访问私有成员
_cout << a._a << endl;
return _cout;
}
(2)友元函数的特征
a. 友元函数可访问类的私有和保护成员,但不是类的成员函数。
b. 友元函数不能用const修饰。
c. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
d. 一个函数可以是多个类的友元函数。
e. 友元函数的调用与普通函数的调用原理相同。
2、友元类
(1)友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
(2)友元关系是单向的,不具有交换性。
(3)友元关系不能传递 如果C是B的友元, B是A的友元,则不能说明C时A的友元。
(4)友元关系不能继承。
class B;
//友元类
class A
{
//B 类作为 A类的友元
friend class B;
public:
void Print(B& b)
{
//A类不可以访问B类 ,不具有交换性
//cout << b._b << endl;
}
private:
int _a = 10;
};
class B
{
friend class C;
public:
void Print(A& a)
{
//可以访问非公有成员
cout << a._a << endl;
}
private:
int _b = 10;
};
class C
{
public:
void Print(A& a)
{
//友元关系不能传递,这里无法访问到A类的非公有成员
//cout << a._a << endl;
}
};
四、内部类
1、概念
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限,但是内部类就是外部类的友元类。
2、特征
(1). 内部类可以定义在外部类的public、protected、private都是可以的,但是也会受到这些作用符限定。
(2)注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
(3) sizeof(外部类)=外部类,和内部类没有任何关系。
//内部类
class A
{
public:
//B类 默认作为 A类友元
class B
{
public:
void P(A& a)
{
//可以直接访问a的非公有成员
cout << a._a << endl;
//可以直接访问A类的静态成员
cout << s_a << endl;
}
private:
int _b = 10;
};
private:
int _a = 10;
static int s_a;
};
int A::s_a = 20;
int main()
{
A a;
cout << "A类的大小:" << sizeof(a) << endl; //4,不受到B类的干扰
//使用B类
A::B b;
//访问b类成员函数
b.P(a);
return 0;
}
五、匿名对象
//匿名对象
class A
{
public:
A(int a = 10)
:_a(a)
{}
int get()
{
return _a;
}
private:
int _a;
};
int main()
{
//正常实例化对象 生命周期从定义到main函数结束
A a1;
A a2(10);
//匿名对象 生命周期仅仅就是定义这行
A();
A(10);
//一些作用如:想调用非静态成员函数
//用匿名对象
A().get();
//一般用法 ,比匿名对象繁琐
A a3;
a3.get();
return 0;
}