C++【类内静态成员】【内置类型与自定义类型的转换】【内部类略讲】【编译器的优化略讲】

静态成员变量与函数

在代码中,有些类中我们希望知道它们调用了多少次构造函数和拷贝构造,比如下行代码

class A
{
public:
	A() { 
		++count;
		cout << "我构造了哦" << endl;
	}
	A(const A& a) {
		++count;
		cout << "我拷贝了哦" << endl;
	}
	~A() {}
private:
	static int count;
};

int A::count = 0;

A func() {
	A aa;
	return aa;
}

int main() {
	A aa;
	func();
	return 0;
}

结果:2次构造函数,0次拷贝构造其实不是,这里编译器进行了优化效果

使用静态成员变量来监视的原因是:

用静态成员变量能够代表全部对象的count

不会像你来一个对象,我专门为你新搞个count出来,这样的话,就不会造成count的混乱,

到时候++的都是同一个count,方便登记。

在类里使用静态成员变量需要注意的是

静态成员变量定义和初始化要分离

类的静态成员变量定义和初始化要分离,它不走初始化列表,需要在类外给定义

原因很简单:静态成员变量只能初始化一次,如果你在类里面给缺省值,就相当于我每创建一个对象,就要对这个静态成员变量初始化一次,显然有违背于静态成员变量规则

类外访问count

如果我在外边想要访问count,情况又会如何?

cout << A::count << endl;

报错了,为什么呢?

注意,这里的A::的作用只是说明了count是属于类里的,并不代表说能在外面访问这个count,此种错误的情况无异于直接在类外写cout<<count<<endl

这种报错情况就是典型的在类外访问成员的私有变量,这是类的私有变量。

如果想要让它访问,以目前见解,有两种做法让它访问

1,让private变为公有

	cout << A::count << endl;
	cout << aa._a << endl;

我们将目光看向aa.count,这里的aa. 并不是说这个count是aa的,只是给出声明,这个aa是属于A的,让编译器去A类里面去寻找count,验证一下

三者访问的是同一个count地址,更能够说明count是属于全类,而不是单属于某个成员对象

就跟类里的成员函数一样,地址并不在成员函数里,只是告诉编译器,这个是属于这个类的 

但我总不能一直公有吧,那我想获取值的话,就得用第二个方法

2,提供getCount函数

使其成为成员函数,达到只读不写的效果

	int getCount() {
		return count;
	}

 

getCount函数所存在的问题

如果我不创建对象,我就调用不了这个getCount这个函数 

可以给匿名对象,匿名对象方式A()

	cout << A().getCount() << endl;
解决方案一:匿名对象

匿名对象的生命周期只有在这一行,意味着只有这一行能用,其他行就不能用了

解决方案二:使用静态成员函数

另一种方法是用静态成员函数,静态成员函数的特点是没有this指针

	static int getCount() {
		return count;
	}

因为有this指针就必须要用对象调用

那现在如何用getCount函数呢

这跟public的情况下,调用A::count差不多的情况

此刻的新问题就是,这个静态的getCount还能不能访问类里面的其它成员变量

答案是不能了,因为没有this指针了

静态成员函数和静态成员变量,本质是受限制的全局变量和全局函数,它们专属这个类,受类域和访问限定符的限制


内置类型隐式转换成自定义类型

单一参数

namespace hl {
	class B {
	public:
		B(int b)
			:_b(b)
		{}
		int _b = 0;
	};
}

void test2() {
	hl::B bb1(1);
	hl::B bb2(2);
	hl::B bb3 = 3;    
    //先用3,构造一个匿名对象B(3),在拷贝构造给bb3
}

 

只要发生类型转换,就会产生临时变量

因为这个转换有一个int的单参数的构造函数支持的 

如果不想让转换成功,构造函数可以加explicit,只防的住隐式转换,强转防不了 

多参数

隐式类型转换也不一定是单参数,还可以是多参数的转换

例如:

	class Date {
	public:
		Date(int year, int month = 1, int day = 1) {
			_year = year;
			_month = month;
			_day = day;
		}
	private:
		int _year, _month, _day;
	};

    void test3() {
	    Date d1 = (2024, 7, 19);
    }    

我们可以清楚的看到_year 被赋值成了19,而_month 和 _day都被赋值成了1

原因何有呢?

逗号运算

因为在执行    hl::Date d1 = (2024, 7, 19); 

的时候,右边的(2024,7,19)会先进行逗号运算,没错,就是逗号运算,最后得到最后一个数字19

所以hl::Date d1 = (2024, 7, 19);         变成了        l::Date d1 = 19;  

而另外两个参数有缺省值给它赋值,所以是两个1

解决方案不能用圆括号,要用花括号{}

友元补充:

友元函数会增添耦合度,即一处出问题,会影响很多处

还有友元类,友元是单向关系,意思就是我是你的朋友,你不一定是我的朋友

内部类:

概念

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

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

特性:

        1,内部类可以定义在外部类的public,protected,private都是可以的。

        2,注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名

        3,sizeof(外部类) = 外部类,和内部类没有任何关系

namespace L {
	class A {
	private:
		int _a;
	public:
		class B {
		public:
			int _b;
		};
	};
}

B就是一个普通类,只不过收到了A类的限制和访问限定符的限制,

B天生就是A的友元,A不是B的友元,即B是A的朋友,但A不是B的朋友,所以B能访问A,A不能访问B

编译器优化

前言

编译器的优化并不是全部编辑器都是一样的,不同的编译器有不同的优化效果,有小优,有大优,具体情况看大家的编译器。

两个已经存在的对象进行赋值,就叫做赋值拷贝

一个已经存在的对象初始化另一个要创建的对象,就是拷贝构造

namespace H {
	class A {
	private:
		int _a;
	public:
		A(int a) {
			cout << "我构造了哦" << endl;
		}

		A(const A& a) {
			cout << "我拷贝构造了哦" << endl;
		}

		~A() {
			cout << "析构一下" << endl;
		}
	};
}

void test5() {
	H::A a1(1);
	H::A a2(a1);
	H::A a3 = a1;
}

正常的很

优化特点:在同一个表达式中 ,普遍会做

  1. 构造+构造 = 构造     
  2. 构造+拷贝 = 构造
  3. 拷贝+拷贝 = 拷贝 

  H::A aa = 1;

按原本来说,1是还要进行一次隐式类型转换,先变成构造函数A类的,并且还要进行一次拷贝构造给aa,此次优化就是属于构造+拷贝 = 构造

这边没有拷贝构造,按原来的道理来说,他应该先构造A(2),紧接着在拷贝构造给aa,但是此处编译器为了省事,使用了优化,即构造 + 拷贝 = 构造

	H::fun(H::A(2));
    //相当于
	H::fun(2);

我这边被狠狠的优化了,原本是 构造 + 拷贝构造 + 拷贝构造 的,现在直接变成构造,用了两次基本优化,这边都跨行优化了,非常狠

构造+构造 = 构造     

构造+拷贝 = 构造

 

这样就只能仅仅在函数那里构造 + 拷贝 = 构造了 ,因为它们不在同一个地方

有些编译器根据检查上下文发现你这个变量没鸟用的时候,同样会直接把它优化掉

A a1(1);
A a2 = a1;
A a3 = a2;

例如这样,编译器会觉得a2根本没有产生的必要,就想着把它给干了,变成下面这样

 

一些构造时的优化,不同的编译器可能会不同

以上便是本次博文的学习内容,如有错误,还望各位大佬能够指正小生,谢谢观看!

相关推荐

  1. C++中不同变量(/ 类型) 初始化规则

    2024-07-20 10:38:03       44 阅读
  2. C++ 类型转换函数

    2024-07-20 10:38:03       35 阅读
  3. JDK 基本注解类型

    2024-07-20 10:38:03       26 阅读
  4. c++ 可以定义引用数据成员吗?

    2024-07-20 10:38:03       49 阅读
  5. C++中定义数据类型和结构体

    2024-07-20 10:38:03       26 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 10:38:03       103 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 10:38:03       114 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 10:38:03       93 阅读
  4. Python语言-面向对象

    2024-07-20 10:38:03       99 阅读

热门阅读

  1. Oracle(12)什么是主键(Primary Key)?

    2024-07-20 10:38:03       25 阅读
  2. 目标检测算法

    2024-07-20 10:38:03       23 阅读
  3. 使用python调用dll库

    2024-07-20 10:38:03       24 阅读
  4. 数据结构之栈、队列和数组的基本概念

    2024-07-20 10:38:03       23 阅读
  5. RoCE(RDMA over Converged Ethernet)网络速率测试工具

    2024-07-20 10:38:03       27 阅读
  6. 读取 Excel 文件

    2024-07-20 10:38:03       20 阅读
  7. 实战:springboot用LocalDateTime快速替换Date

    2024-07-20 10:38:03       19 阅读
  8. Spark的部署模式

    2024-07-20 10:38:03       28 阅读