【C++】 初始化列表 / explicit / static成员 / 友元

本文介绍了C++中构造函数的初始化列表,包括其发明缘由、语法和必要性,特别强调了const变量、引用以及无默认构造函数的自定义类型的初始化。另外,讨论了explicit关键字的作用,防止隐式类型转换。还涵盖了静态成员变量和函数的使用,以及友元函数和友元类的概念和特性。文章最后提及了内部类作为外部类的友元的特性。
摘要由CSDN通过智能技术生成

初始化列表

初始化列表是构造函数的一种形式

1.发明缘由

类的成员变量的声明和定义是不同时的
在这里插入图片描述

只有在实例化对象时,成员变量才会开始定义

在这里插入图片描述
这样也并不是定义,1和2是类似函数的缺省值,并不是赋值。如果初始化时没有赋值,缺省值才会使用

我们知道const变量和引用的定义必须和声明同时完成,所以如果成员变量有这几个的话,那么类对象将无法完成实例化。所以C++祖师爷开发了初始化列表

2.语法

每个成员变量最多定义一次
在这里插入图片描述
在这里插入图片描述

int _a1=1和int _a2=2是缺省,如果初始化列表没有初始化,则使用缺省值,所以_a1=1,_a2=2。

再比如
在这里插入图片描述
在这里插入图片描述

如上,_a1=1,_a2=1。然后执行大括号里的内容,_a1++,_a2–。所以最后_a1=2,_a2=0。

3.补充

上述有两个必须要在初始化列表定义的变量
1.const变量
2.引用

其实还有第三个。
结合构造函数的知识点,默认构造函数,对于内置类型不做处理,对于自定义类型,会调用其构造函数。
在这里插入图片描述
B类的构造函数是有参构造函数,B类在A类中,A类会去调用B类的构造函数,但因为是有参构造,所以必须传参,但因为A类的初始化列表没有给B的构造函数传参,所以报错。

所以,对于没有默认构造的自定义类型,也必须在初始化列表定义。

注意:成员变量的定义顺序是同声明一样

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

初始化列表是先定义_a2,然后再定义_a1。所以cout的结果是,_a2是随机值,_a1是1。

explicit

发明缘由

class A
{
public:
	A(int aa)
		:_a1(aa)
	{
		cout << "构造函数" << endl;
	}

    A(const A&aa)
		:_a1(aa._a1)
	{
		cout << "拷贝构造" << endl;
	}
private:
	int _a1;
	int _a2;
};


int main()
{
	A aa1(1);//构造函数
	A aa2 = 1;//隐式类型转换

	int i = 1;
	double d = i;//隐式类型转换

	return 0;
}

aa1(1)和aa2=1其实是不一样的
aa2=1其实和int赋值给double那样,会创建临时变量,然后再通过拷贝构造函数,进行赋值
在这里插入图片描述

但C++11会进行优化,原本是拷贝+构造,优化后,就只通过构造就进行了赋值

在这里插入图片描述

我们可以拒绝这样的隐式转换,就是通过explicit关键字

class A {
public:
    // 隐式转换构造函数
    A(int x) : _x(x) {}

    int getX() const { return _x; }

private:
    int _x;
};

int main() {
    A a = 10; // 编译器会隐式调用 A(int x) 构造函数
    cout << a.getX() << endl; // 输出 10

    // 使用 explicit 关键字后
    class B {
    public:
        explicit B(int x) : _x(x) {}

        int getX() const { return _x; }

    private:
        int _x;
    };

    // B b = 20; // 错误,不能隐式调用 explicit 构造函数
    B b(20); // 必须显式调用构造函数来创建对象
    cout << b.getX() << endl; // 输出 20

    return 0;
}

3.编译器版本影响

以上都是单参数的构造函数,C++98支持单参数的隐式类型转换
但是不支持多参数
在这里插入图片描述

在这里插入图片描述
但是C++11支持,不过形式不同
是将传参用花括号包含
在这里插入图片描述

static成员变量&成员函数

1.引子

如果让我们创建一个可以知道实例化了几个对象的类,我们怎么写?
第一种方法,我们可以在全局域创建一个全局变量。
第二种方法,我们可以使用static成员变量和成员函数

class A
{
public:
	A(int aa)
		:_a1(aa)
	{
		count++;
		cout << "构造函数" << endl;
	}

	A(const A&aa)
		:_a1(aa._a1)
	{
		count++;
		cout << "拷贝构造" << endl;
	}

	//静态成员函数,访问静态成员变量
	static int GetCount()
	{
		return count;
	}

private:
	int _a1;
	int _a2;

	//静态成员变量既属于类,又属于每个对象
	//如果设为公有,那么既可以通过对象访问,也可以通过类访问
	static int count;//声明
};

int A::count=0;//定义

首先,
一 . static成员变量,以下称为静态成员变量。
他的定义和声明较为特殊,且声明不能有缺省值定义需要在全局区定义

二. 静态成员变量既属于类,又属于每个对象。即如果为公有,则既可以通过对象访问,又可以通过类访问

比如

int main()
{
    //如果count为公有
    A aa1(1);
    cout<<aa1.count<<endl;
    cout<<A.count<<endl;
}

如果静态成员变量是私有的,那么则需要静态成员函数对其操作。
静态成员函数没有this指针

//静态成员函数,访问静态成员变量
//没有this指针
static int GetCount()
{
	return count;
}

int main()
{
    //支持直接通过类访问
    //如果没有设置为静态,则必须通过对象来调用
    cout << A::GetCount() << endl;
}

2.小总结

1.静态成员所有类对象所共享,不单独属于某个具体的对象,存放在静态区
2.静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3.类静态成员即可用 类名 : : 静态成员或者 对象 . 静态成员来访问
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员也是类的成员,受public,protected,private访问限定符的限制

友元

友元可分为友元函数友元类

1.友元函数

在编写 运算符重载实践 — 日期类流插入流提取时,我们就使用过友元函数,效果是,可以让全局函数访问类中的私有成员变量。关键字是friend,此处不再阐述语法

说明:
1.友元函数可以访问类的私有和保护成员,但不是类的成员函数
2.友元函数不能用const修饰
3.友元函数可以在类定义的任何地方声明不受类访问限定符限制(public等)
4.一个函数可以是多个类的友元函数
5.友元函数的调用与普通函数的调用原理相同

2.友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

class A
{
    //B是A的友元类,B可以访问A的成员,但A不能访问B的成员
	friend B;
public:
	A()
		:_a1(1)
		,_a2(1)
	{}

private:
	int _a1;
	int _a2;
};

class B
{
public:
	B()
		:_b1(1)
		,_b2(1)
	{}

private:
	int _b1;
	int _b2;
};

注意:
1.友元关系是单向的,不具有交换性,B是A的友元,但A不一定是B的友元
2.友元关系不能传递,B是A的友元,C是B的友元,A和C不一定有关系
3.友元关系不能继承,详细将会在继承继续学习

3.内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限

注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的独享参数来访问外部类中的所有成员。但是外部类不是内部类的友元

特性:
1.内部类可以定义在外部类的public等,但是会受其约束
2.注意内部类可以直接访问外部类的static成员,不需要外部类的对象/类名
3.sizeof(外部类)=外部类的大小,和内部类没有关系,在空间上,两者是互相独立的

class A
{
private:
	static int k;
	int h = 1;

	// 内部类 -- 跟A是独立,只是受A的类域限制
	// B天生就是A的友元
public:
	class B
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	private:
		int b = 2;
	};
};

int A::k = 1;

int main()
{
	A aa;
	cout << sizeof(aa) << endl;//4个字节,只代表A的大小,和B无关

	A::B bb;

	return 0;
}

本章用于记笔记,如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值