【C++进阶之路】类和对象(下)

news2024/11/28 16:41:36

文章目录

  • 一.再谈构造函数
    • 初始化对象的两种方式
      • 1.函数体内赋值
      • 2.初始化列表
        • 深入理解初始化列表
      • 3.explicit
        • 隐式类型转换
  • 二.static成员
    • 引言
    • static成员
    • static成员函数
    • 练习
  • 三.友元
    • 引入
    • 友元函数
    • 友元类
  • 四.内部类
    • 基本概念
    • 练习
  • 五 .匿名对象
    • 引入
    • 匿名对象
    • 补充:编译器的优化

一.再谈构造函数

初始化对象的两种方式

我们必须清楚的记住:
1.定义一个类,类里面的成员变量只是一个声明。——图纸
2.用类名定义一个对象,这叫做对象的实例化——造房子

1.函数体内赋值

#include<iostream>
using namespace::std;
class A
{
public:
	A(int a = 1, int b = 1, int c = 1)
	{
		_a = a;
		_b = b;
		_c = c;
	}
private:
	int _a;//这里只是声明
	int _b;
	int _c;
};
int main()
{
	A a;
	return 0;
}
  • 在函数体内部将对象成员变量赋值的方式我们称之为函数体内赋值
  • 严格上来说,在对象空间创建的同时给成员变量一个值,这才叫初始化。
  • 而函数体内赋值更像是对象创建再给成员变量值,这叫赋值操作。
  • 举我们学过的例子
    1. 引用必须初始化——总得给取个外号吧,这个谁总得有吧
    1. auto必须初始化——总得识别一个数才能知道其类型吧,这个数总得有吧
    1. const修饰的变量必须初始化——总得有个确定的数吧,这个数总得有吧,后面再给就不合适了。
    1. 自定义的对象有构造函数但不是默认构造也得初始化 ——总得给其成员一个确定的值吧,构造函数不能在定义之后再调用吧?
  • 总结:我们可以理解为这样规定是为了安全性考虑的

为了验证是不是我们想的那样我们可以try一下。

#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;
};
class A
{
public:
	A(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
		
		_d = 1;
		int x = 0;
		_e = x;//这个引用的例子不太恰当,看一下就行
		//下面这三个如果是初始化的话,编译器是不会报错的
	}

private:
	int _a;
	int _b;
	int _c;
	const int _d;
	int& _e;
	Date _f;
};

编译一下:
在这里插入图片描述

  • 因此:构造函数内部进行的成员赋值不是初始化!

  • 那对上述如何进行初始化呢?

2.初始化列表

  • 初始化列表长什么样子呢?
  • 冒号开始后跟成员再加括号——括号里放着初始值
  • 接着都是逗号开始后跟成员再加括号
  • 逻辑表达式:
  • :成员变量(初始值)
  • , 成员变量(初始值)

具体代码

class A
{
public:
	A(int a, int b, int c,int x)
		:_a(a)
		,_b(b)
		,_c(c)
		,_d(1)
		,_e(x)//这个例子不太恰当,应该函数参数x应该设置为引用。
		,_f(2023,5,14)//此处调用的是构造函数
	{

	}
private:
	int _a;
	int _b;
	int _c;
	const int _d;
	int& _e;
	Date _f;
};

再次编译一下:
在这里插入图片描述
很显然没有报错

  • 因此:初始化列表是成员定义的地方
  • 注意: 一个成员最多初始化一次,也就是说有的成员是可以不用显式的写在初始化列表,但是有的成员是必须显式的出现在初始化列表中。——总结关键:显式
  • 说明:必须显式的出现在初始化列表的成员有:
    1. 引用
    1. const修饰的变量
    1. 有构造函数但不是默认构造函数的自定义类型
  • 具体原因上面已经说明。

继续分析C++11引出的一个补丁:

  • 编译器生成默认构造函数对内置类型不做处理,但我们可以在声明处给缺省值
class Date
{
public:
private:
	int _year = 1;//这是缺省值,可不是初始化
	int _month = 1;
	int _day = 1;
};
  • 既然是缺省值,那这个缺省值给了谁呢?
  • 很显然有了前面的铺垫,我们应该能很容易的想出这个缺省值是给了初始化列表的
  • 但是:这里我们没写初始化列表。
  • 因此:即使不写初始化列表,也会走初始化列表,因为初始化列表是成员定义的地方!

  • 那如果我们显示写一下初始化列表:
class Date
{
public:
	Date()
		:_year(2)
		,_month(2)
		,_day(31)
	{

	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};
  • 这时候我们该用哪一个值呢?
  • 缺省值,顾名思义就是备胎,人家正谈呢那轮的到备胎啊,所以初始化列表显示赋值就用那个值,没赋值才用缺省值

  • 那么新问题出现了——既然初始化列表可以初始化,那还要函数体有何用呢?
  • 其实不然,比如我们写一个栈对象,初始化时,对malloc返回的值要进行检查,那么这个检查的工作谁来做呢?当然是函数体了!

代码:

class Stack
{
public:
	Stack(int capacity = 10)
		:_a((int*)malloc(sizeof(int)*capacity))
		,_top(0)
		,_size(capacity)
	{
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
	}
private:
	int* _a;
	int _top;
	int _size;
};
  • 因此:我们要根据实际情况进行灵活应对这些情况。

深入理解初始化列表

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();
}
  • 请问此代码的执行结果是什么?
    在这里插入图片描述
  • 答案: 1和随机值
  • why?listen to me carecfully!


看这样的调试我们注意仔细看初始化成员列表的那两行代码,按照我们的想法不是应该先走_a1再走_a2吗?但为啥先走_a2再走_a1?

  • 答案其实很简单,走初始化列表的顺序与你写初始化列表的顺序无关只与类中声明成员变量的顺序有关。
    具体声明顺序是什么呢?
    在这里插入图片描述

  • 如何避免这样的理解错误发生呢?

  • 也很简单,声明的顺序与写初始化列表的顺序一致即可。

改进代码:

class A
{
public:
	A(int a)
		: _a2(a)
		, _a1(_a2)
	{}
	void Print() 
	{
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};

int main() 
{
	A aa(1);
	aa.Print();
}

再次执行:
在这里插入图片描述

3.explicit

隐式类型转换

class Date
{
public:
	Date(int year = 1)
	{
		year = 1;
	}
	//拷贝构造
	Date(Date& B)
	{
		_year = B._year;
	}
	void Print() const//D类型为const因此this指针也必须为const类型的 
	{
		cout << _year << endl;
	}
private:
	int _year;
};
int main()
{
	Date A;//调用默认的拷贝构造
	Date B = A;//调用的是拷贝构造,本质上为Date B(A);
	Date C = 1;//隐式类型转换,先将1转换为一个Date类型的临时变量,再将临时变量拷贝到C中
	//但是在编译器运行时,为了提升效率会将这两步合二为一调用构造函数。
	//需要特别注意的是——这里的1发生隐式类型转换的条件为,得有合适的拷贝构造。
	const Date& D = 1;//这里也发生隐式类型转换,将1转换为Date类型的临时变量,
	//不要忘记这里的临时对象具有常属性!
	//因此要加上const,但这里只是个临时变量,那是否会销毁呢?
	//打印一下
	D.Print();
	return 0;
}

程序运行结果:
在这里插入图片描述

  • 很显然这里的临时变量销毁了,因此这里的引用是十分危险的,因为访问了一块被销毁的空间。

  • 有没有方法不让隐式类型转换发生呢?

  • 答案显然是有的,那就是在构造函数的前面加上explicit

class Date
{
public:
	explicit Date(int year = 1)
	{
		year = 1;
	}
	//拷贝构造
	Date(Date& B)
	{
		_year = B._year;
	}
	void Print() const
	{
		cout << _year << endl;
	}
private:
	int _year;
};
int main()
{
	Date A;
	Date B = A;
	Date C = 1;
	const Date& D = 1;
	D.Print();
	return 0;
}

编译一下:
在这里插入图片描述

  • 很显然,编译器在编译的阶段就阻止了我们发生隐式类型转换。

二.static成员

引言

为了引出static成员我提出一个问题,如何实现一个确定我们当前正在使用的对象的个数的程序?

  • 原理:
  • 每次生成一个对象,都会调用其构造函数,那么我们只需要设置一个变量初始化为0,在每次调用构造函数时加1即可。那这个变量是什么类型才能满足我们的需求呢?
  • 一般我们都会设置一个全局变量,这样哪都能访问。

实现代码:

int count_obj = 0;
class Date
{
public:
	Date(int year = 1)
	{
		_year = 1;
		count_obj++;
	}
	Date(Date& B)//拷贝构造函数本质上是构造函数的重载形式
	{
		_year = B._year;
		count_obj++;//我们也要在拷贝构造里面加加
	}
	~Date()
	{
		count_obj--;
	}
private:
	int _year;
};
void func(Date C)//这里只会调用拷贝构造,也会调用析构函数,不会调用构造函数。
{
	Date D;
	cout << __LINE__ << ":" << count_obj << endl;
}
//这里的C和D都会销毁,你还记得销毁的顺序吗?
//与构造函数的调用顺序相反,这里的D先销毁然后C再销毁
int main()
{
	Date A;
	cout << __LINE__ << ":" << count_obj << endl;
	Date B;
	cout << __LINE__ << ":" << count_obj << endl;
	func(B);
	cout << __LINE__ << ":" << count_obj << endl;
	return 0;
}
  • 补充: __LINE__——显示当前的行数

运行一下:
在这里插入图片描述

  • 还用上面的类,加上下面的代码。
void func()
{
	static Date D;
	cout << __LINE__ << ":" << count_obj << endl;
}
int main()
{
	Date A;
	cout << __LINE__ << ":" << count_obj << endl;
	Date B;
	cout << __LINE__ << ":" << count_obj << endl;
	func();
	func();
	cout << __LINE__ << ":" << count_obj << endl;
	return 0;
}

执行一下:
在这里插入图片描述

static成员

  • 到这里问题就结束了吗?显然没有,接着深入分析,既然我们创建了此全局变量,那么别人是不是想用就用了,这样代码就不是很安全了,那如何将此全局变量封装成只有该类能用的全局变量呢?那就轮到static出手了。
  • 如何操作?且听我娓娓道来。
  • 我们首先应该了解static的定义:
  • 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量
  • 用static修饰的成员函数,称之为静态成员函数
  • 静态的成员变量一定要在类外进行初始化

那如何操作呢?按照定义我们应该在类里面声明一个static成员,然后再类外初始化
具体代码:

class Date
{
public:
	Date(int year = 1)
	{
		_year = 1;
		_count_obj++;
	}
	~Date()
	{
		_count_obj--;
	}
private:
	int _year;
	static int _count_obj;//这里只是声明而不是定义。
	//static修饰的变量存储在静态区,那么不随对象示例化的存在而存在,也不随对象销毁而销毁。
	//因此:static修饰的变量属于所有该类的对象。属于共享成员。
};
int Date::_count_obj = 0;//这是用域作用限定符进行初始化。
int main()
{
	Date A;
	return 0;
}

static成员函数

  • 这里如果我们想打印一下_count_obj呢?
  • 由于这里是私有成员,那么就只能通过成员函数就能访问。

实现代码:

class Date
{
public:
	Date(int year = 1)
	{
		_year = 1;
		_count_obj++;
	}
	~Date()
	{
		_count_obj--;
	}
	void Print_Using_Obj()
	{
		cout<<__LINE__<<":" << _count_obj << endl;
	}
private:
	int _year;
	static int _count_obj;
};
int Date::_count_obj = 0;
  • 但是这里又有一个问题就是这个函数必须通过对象才能访问,那有没有什么方法不用通过对象访问,而是通过类域直接访问呢?
  • 当然有了设置一个static函数!
class Date
{
public:
	Date(int year = 1)
	{
		_year = 1;
		_count_obj++;
	}
	~Date()
	{
		_count_obj--;
	}
	//说明这里只是多了一种访问方式——既可以通过对象访问,也可以通过类域进行访问。
	//但要注意一点这里的函数是没有this指针的,也就是说不能用const限定符进行修饰。
	static void Print_Using_Obj()
	{
		cout<<__LINE__<<":" << _count_obj << endl;
	}
private:
	int _year;
	static int _count_obj;
};
int Date::_count_obj = 0;
int main()
{
	Date::Print_Using_Obj();
	return 0;
}

总结:

  • 一. static 成员
    1. static修饰的成员必须在类外,用域作用限定符进行初始化定义不用加static
    1. static修饰的成员是所有类对象共享的,并不属于某一个特定的对象。
    1. static修饰的成员若为私有只能通过成员函数进行访问
    1. static成员是不会走初始化列表的!因此不能给static缺省值.
  • 二. static修饰的成员函数
    1. 没有this指针,间接说明不能用const进行修饰。也不能访问成员变量(不加static)。
    1. 可以通过类域进行访问,也可以通过对象进行访问,其本质都是突破类域进行访问

练习

  • 求1+2+3+…+n
  • 条件:
  • 1.不能使用乘除法、
  • 2.for、while、if、else、switch、case等关键字
  • 3.条件判断语句(A?B:C)。
  • 此外我们再加几条:
    1. 不得使用异或
    1. 不得使用递归
      那我们可以用什么呢?当然是static了!

具体如何实现呢?

class Sum 
{
public:
    Sum()
    {
        _n+=_i;
        _i++;
        //每次创建一次_n加上i同时_i加等上1这样就达到了累加求和的效果。
    }
    static int GetSum()
    {
        return _n;
    }
private:
   static int _n;
   static int _i;
};
int Sum::_n = 0;
int Sum::_i = 1;
class Solution {
public:
    int Sum_Solution(int n) 
    {
        Sum sum[n];//这里是变长数组
        //如果编译器不支持变长数组可以这样写
        return Sum::GetSum();//返回的是_n
    }
private:
};

三.友元

引入

  • 当我们实现日期类的输入和输出时,如果放在类里面进行实现,那要怎么实现呢?

实现代码:

class Date
{
public:
	ostream& operator<<(ostream& out)
	{
		out << _year << "年" <<_month << "月" << _day << "日" << endl;
		return out;
	}
	istream& operator>>(istream& in)//——>(Date * const this, istream &in)
	{
		cin >> _year >> _month >> _day;
		return in;
	}
private:
	int _year = 1949;
	int _month = 10;
	int _day = 1;
};
  • 但是这样要如何使用呢?
  • 由于:这里本质上是两个参数——this指针和输出/输入,并且先后顺序不能变
int main()
{
	Date A;
	A >> cin;//跟我们一般使用的cin<<A;相反
	A << cout << endl;//跟我们一般使用的cout<<A;也相反。
	return 0;
}

看这样调用是不是很别扭?

  • 如何改成我们想要的形式呢?
  • 这就引出了友元函数。

友元函数

  • 概念:
  • 友元函数可以直接访问类的私有成员
  • 它是定义在类外部的普通函数不属于任何类
  • 需要在类的内部声明,声明时需要加friend关键字

接着改进我们上面的代码:

class Date
{
	friend ostream& operator<<(ostream& out, const Date& date);
	friend istream& operator>>(istream& in, Date& date);
public:

private:
	int _year = 1949;
	int _month = 10;
	int _day = 1;
};
ostream& operator<<(ostream& out, const Date& date)
//打印时不需要对类里面的成员进行修改,因此对象要用const进行修饰
{
	out << date._year << "年" << date._month << "月" << date._day << "日";
	//直接访问类里面的成员,不管是私有还是公有。
	return out;
}
istream& operator>>(istream& in, Date& date)
//输入要对对象进行修改,因此不加const
{
	cin >> date._year >> date._month >> date._day;	
	return in;
}
int main()
{
	Date A;
	cin >> A;
	cout << A << endl;;
	return 0;
}
  • 这样就舒服很多了。

说明:

  • 友元函数不是成员函数,因此不受类访问限定符(pubilc,private,protect)的限制。
  • 友元函数没有this指针,因此不能用const进行修饰
  • 多个类可公用一个友元函数,因此提高了类的耦合度——降低了类的独立性
  • 友元函数就是普通的函数,因此跟一般的函数调用无区别。

友元类

  • 友元类是指一个类所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员
  • 友元关系是单向的,也就是说我认识你,你不认识我,转换到友元类中就是友元类可以访问另一类的所有成员,但另一个类不一定访问友元类的所有成员。
  • 友元关系不能传递,也就是我认识你,你认识他,但我不认识他。转换到友元类,类1是类2的友元,类2是类3的友元,则类1不是类3的友元。
  • 这几个点很重要!!!

  • 友元类涉及到定义与声明的问题。
  • 一个故事让你彻底理解友元类!

为了了解清楚我们分析一段错误的友元类写法

class Date;
class Time
{
public:
	void Print(Date A)
	{
		cout << A._year << endl;
	}
private:
	int _hours = 1;
	int _minutes = 1;
	int _seconds = 1;
};
class Date
{
public:
	friend class Time;
private:
	int _year = 1949;
	int _month = 10;
	int _day = 1;
};

在这里插入图片描述

  • 那正确的应该怎么写呢?
class Time;
class Date
{
public:
	friend class Time;
private:
	int _year = 1949;
	int _month = 10;
	int _day = 1;
};
class Time
{
public:
	void Print(Date A)
	{
		cout << A._year << endl;
	}
private:
	int _hours = 1;
	int _minutes = 1;
	int _seconds = 1;
};
int main()
{
	Date A;
	Time B;
	B.Print(A);
	return 0;
}

在这里插入图片描述

四.内部类

基本概念

  • 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
  • 注意:
    1. 内部类不属于外部类
    1. 外部类对内部类没有任何优越的访问权限。
    1. 内部类天然就是外部类的友元类(规定)。内部类——友元类是单向的

具体代码:

class Date
{
public:
	class Time
	{
	public:
		void Print(Date A)
		{
			cout << A._year << endl;
		}
	private:
		int _hours = 1;
		int _minutes = 1;
		int _seconds = 1;
	};
private:
	int _year = 1949;
	int _month = 10;
	static int _day;
};
int Date::_day = 1;
int main()
{
	Date A;
	Date::Time B;
	B.Print(A);
	return 0;
}
  • 外部类与内部类唯一的关系,恐怕就是在定义一个内部类的时候了,因为在类域里面所以要通过域作用限定符进行对内部类对象的实例化。

练习

  • 以下代码的输出结果为?
class Date
{
public:
	class Time
	{
	public:
		void Print(Date A)
		{
			cout << A._year << endl;
		}
	private:
		int _hours = 1;
		int _minutes = 1;
		int _seconds = 1;
	};
private:
	int _year = 1949;
	int _month = 10;
	static int _day;
};
int Date::_day = 1;
int main()
{
	cout << sizeof(Date) << endl;
	return 0;
}

执行一下:
在这里插入图片描述

  • 观众们答对了吗?
  • 回答错的别急,下面我们就细讲为什么?

  • 前提:sizeof——求的是类实例化对象的大小

  • 第一:内部类不属于外部类,因此内部类的空间不属于外部类。

  • 第二:static 修饰的成员不属于类对象,而属于整个类,且存储空间不在类里面,而在静态区。

  • 第三:成员函数的空间存储在代码区,也不在类里面,属于类对象共享。

  • 因此:只有_year和_month在类里面创建,单独属于每个类对象,所以共8字节

  • 说明:友元的声明只是说明谁是谁的友元,而并不能说明谁属于谁

五 .匿名对象

引入

为了更好的理解下面的知识,我们复习一下匿名结构体

  • 定义结构体时,不写结构体的名字。
struct
{
	int _month;
}stu1;//不写结构体名字,直接用结构体名字创建变量。

class
{

public:
	int _year = 1;
}s;//匿名类,直接用类名创建对象。
int main()
{
	cout << s._year << endl;
	cout << stu1._month << endl;
	return 0;
}

匿名对象

由上面的匿名结构体可知:

  • 匿名对象——对象创建时不写名字

代码:

class Date
{
public:
	Date(int year)
		:_year(1949)
		,_month(10)
		,_day (1)
	{
		cout << "Date()" << endl;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date A(1949);//有对象名,所以叫有名对象
	Date(1949);//没有对象名,所以叫匿名对象
	return 0;
}

说明:匿名对象的格式——类名+(值);
注意:这个值有默认构造可不写

  • 那匿名对象和有名对象有什么区别吗?
  • 答案肯定是有的,我们调试一波。

在这里插入图片描述

  • 这样我们不难看出:
  • 因为在走过Date(1949)之后,调用了构造函数和析构函数
  • 因此:匿名对象的生命周期在其存在的那一行。
  • 那不少观众就要问了——既然匿名对象如此短命,那么匿名对象有啥用呢?
  • 肯定是有用的,只是我们的见过的东西太少了以至于不太了解这个东西的用法。
  • 简单说明一个用法:
    当我们想用对象的一个函数,而又不想额外的开辟一个对象——之后这个对象就不用了,进行调用。那我们要怎么办呢?——用一个随用随弃的对象(匿名对象)
class Date
{
public:
	Date(int year)
		:_year(1949)
		,_month(10)
		,_day (1)
	{
		cout << "Date()" << endl;
	}
	void Func(int n)
	{
		//实现的功能
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date A(1949);
	A.Func(50);
	Date(1949).Func(50);//像这样进行调用,调用完就释放了,这样就节省了一部分的开销。
	return 0;
}
  • 既然匿名对象的周期很短,那能不能延长它的生命周期呢?
  • 答案是:有的,吃一颗伸腿瞪眼丸——const +引用
class Date
{
public:
	Date(int year)
		:_year(1949)
		,_month(10)
		,_day (1)
	{
		cout << "Date()" << endl;
	}
	void Print()const
	{
		cout << _year << "年" << _month << "月" << _day << "天" << endl;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	const Date& date = Date(1949);

	date.Print();
	//这里所传的this指针是const Date* const 类型的,因此Print函数需要用const修饰一下。

	return 0;
}

来调试一下:

在这里插入图片描述

  • 我们可以看到,在匿名对象进行引用加const之后,在之后析构函数没有调用
  • 在main函数结束之后才进行调用析构。
  • 结论1:加了const与引用匿名对象的生命周期延长为当前函数局部域
  • 注意:续命之后的匿名对象只能调用const修饰的成员函数
  • 结论2: 之所以加上const是因为匿名对象具有常属性

补充:编译器的优化

  • 编译器通常会将连续的构造进行优化
  • 注意:
    1. 连续指的是在同一条语句
    1. 这里的构造指的是——拷贝构造和构造

例1代码:

class Date
{
public:
	Date(int year = 1949)
		:_year(year)
	{
		cout << "Date()" << endl;
	}
	Date(const Date& B)
	{
		_year = B._year;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
};
Date Func()
{
	Date A;

	return A;

}
void Func1(Date A)
{
	
}
int main()
{

	Date A = Func();
    //这里是一个拷贝构造+拷贝构造优化为拷贝构造
	return 0;
}

执行结果:
在这里插入图片描述
图解:
在这里插入图片描述

  • 说明:以下代码沿用上面的类和函数。

例2代码:

int main()
{
	Func1(Date());
	return 0;
}

执行结果:
在这里插入图片描述
图解:
在这里插入图片描述
例3代码:

int main()
{
	Date D = 1;
	return 0;
}

执行结果:
在这里插入图片描述
图解:
在这里插入图片描述

int main()
{
	Date D = 1;
	return 0;
}


例4:

int main()
{
	Func1(1);
	return 0;
}

运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

例5:

int main()
{
	Date A(2020);
	Func1(A);
	return 0;
}

执行结果:
在这里插入图片描述
图解:
在这里插入图片描述

  • 总结:常属性
  • 匿名对象具有常属性
  • 返回值具有常属性
  • 隐式类型转换的结果具有常属性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一百一十三、DBeaver——从hive同步数据到clickhouse

目标&#xff1a;把hive的DM层结果数据导出到clickhouse数据库&#xff0c;试了kettle、sqoop等多种方法都报错&#xff0c;尤其是kettle&#xff0c;搞了大半天发现还是不行。结果目前就只能用DBeaver同步数据。 准备工作&#xff1a;hive和clickhouse中都建好表 第一步&…

$nextTick与原生js先渲染dom再执行函数的方式

$nextTick与原生js先渲染dom再执行函数的方式 vue的$nextTick 简述 Vue.js 的 $nextTick 方法是用于在 DOM 更新后执行延迟回调的方法。 当 Vue.js 更新 DOM 后&#xff0c;有时候需要执行一些操作&#xff08;如获取更新后的 DOM 元素的位置等&#xff09;&#xff0c;但是…

2023年美国大学生数学建模竞赛C题预测Wordle的结果解题全过程文档及程序

2023年美国大学生数学建模竞赛 C题 预测Wordle的结果 原题再现&#xff1a; 背景介绍   Wordle是纽约时报目前每天提供的一个流行谜题。玩家通过在六次或更短时间内猜出一个至五个字母的单词来解决这个谜题&#xff0c;每次猜测都会得到反馈。在这个版本中&#xff0c; 每个…

30Wqps+闲鱼优惠中台,如何架构的?

说在前面 在尼恩的&#xff08;50&#xff09;读者社群中&#xff0c;经常遇到一个 非常、非常高频的一个面试题&#xff0c;但是很不好回答&#xff0c;类似如下&#xff1a; 千万级数据&#xff0c;如何做系统架构&#xff1f;亿级数据&#xff0c;如何做做系统架构&#xf…

2022年美国大学生数学建模竞赛E题森林的碳封存解题全过程文档及程序

2022年美国大学生数学建模竞赛 E题 森林的碳封存 原题再现&#xff1a; 背景   正如我们所知&#xff0c;气候变化对生命构成了巨大威胁。为了减轻气候变化的影响&#xff0c;我们需要采取有效的行动来减少大气中温室气体的含量。仅仅减少温室气体排放是不够的。我们需要努…

Redis可持久化详解2

目录 ​编辑 Redis的持久化配置参数&#xff1a; 2.Redis的性能问题&#xff1a; 3保持久化数据的完整性和正确性&#xff1a; 4.Redis的集群技术&#xff1a; 总结&#xff1a; Redis持久化不得不注意的一些地方。 Redis的持久化配置参数&#xff1a; save&#xff1a;指…

MySQL之InnoDB表空间

前言 本文章收录在MySQL性能优化原理实战专栏&#xff0c;点击此处查看更多优质内容。 本文摘录自 ▪ 小孩子4919《MySQL是怎样运行的&#xff1a;从根儿上理解MySQL》 通过前边的内容&#xff0c;相信大家都知道了表空间是一个抽象的概念&#xff0c;对于系统表空间来说&am…

Unity 全新的输入系统

选new的那个 会重启ide 然后去package manager里把这个装上 创建配置文件 文档地址 https://docs.unity3d.com/Packages/com.unity.inputsystem1.5/manual/index.html 创建完了是这样的 设置成这样 记住 value 和 vector2 然后就可以 选择 上下左右了 之前不设置 value…

【macOS】:macbook 安装了两个macOS后,设置默认启动的顺序

【macOS】&#xff1a;macbook 安装了两个macOS后&#xff0c;设置默认启动的顺序 如果你的 MacBook 安装了两个 macOS 操作系统&#xff0c; 你可以通过以下步骤设置默认启动的顺序&#xff1a; 1. 点击苹果菜单&#xff0c;选择“系统偏好设置”。 2. 点击“启动磁盘”选…

基于OpenMP的并行计算实验

一、实验目的 在Linux系统中&#xff0c;基于C编写OpenMP用例对并行计算进行实验&#xff1a; 1.掌握OpenMP的配置方式&#xff1b; 2.通过编写测试用例&#xff0c;观察引入OpenMP的加速效果 二、实验内容 2.1配置并测试OpenMP 1.配置并测试OpenMP 配置环境变量&#x…

MySQL基础(三十五)多版本并发控制

1. 什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并发控制。顾名思义&#xff0c;MVCC 是通过数据行的多个版本管理来实现数据库的 并发控制 。这项技术使得在InnoDB的事务隔离级别下执行 一致性读 操作有了保证。换言之&…

乡镇外卖好做吗?这几点一定要注意

乡镇外卖可靠吗&#xff1f;随着90后逐渐成为社会的主力军&#xff0c;自主创业&#xff0c;不做农民工已经成为这一代人的标签。找到一个项目难&#xff0c;可靠的项目更难找。根据实际数据反馈&#xff0c;乡镇外卖的就是是一个小而精细的项目。 但如果想通过乡镇外卖每月挣…

Python获取酷得music并下载,获得无限听

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 环境使用: Python 3.8 Pycharm 模块使用: requests >>> pip install requests re win R 输入cmd 输入安装命令 pip install 模块名 (如果你觉得安装速度比较慢, 你可以切换国内镜像源) 代码实现步骤 <基…

H桥电机驱动芯片CS9027C可pin对pin兼容DRV8812

CS9027C为打印机和其它电机一体化应用提供一种双通道集成电机驱动方案。CS9027C有两路 H 桥驱动&#xff0c;每个H桥可提供最大输出电流2A(在24V 和Ta 25℃ 适当散热条件下)&#xff0c;可驱动两个刷式直流电机&#xff0c;或者一个双极步进电机&#xff0c;或者螺线管或者其它…

全网火爆,性能测试从0到1分析需求到实战详解,冲出高级测试岗...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、制定测试目的 …

《设计模式》备忘录模式

《设计模式》备忘录模式 定义&#xff1a; 备忘录模式又称为快照模式或者令牌模式&#xff0c;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;这样可以在以后将对象恢复到原先保存的状态。属性行为型模式。 备…

【云原生】私有镜像的解决方案-Harbor管理实战

前言 通过前面的镜像管理&#xff0c;我们知道docker的开发和运行都离不开镜像管理&#xff0c;如果要进行镜像管理就需要使用镜像仓库。 docker官方提供的镜像仓库是&#xff1a; docker hub&#xff0c; 很方面但从安全和效率的角度考虑&#xff0c;这样的公有镜像环境就无…

Qt文件系统源码分析—第一篇QFile

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice QIODev…

实用技能 || 从安装到实战!Citespace、vosviewer保姆级教程!

【基于Citespace和vosviewer文献计量学相关论文 】 专题一&#xff1a;文献计量学方法与应用 1 文献计量学方法基本介绍 2 与其他综述方法区别联系 3 各学科领域应用趋势近况 4 主流分析软件优缺点对比 5 经典高分10SCI思路复盘 6 软件安装与Java环境配置 专题二&#…

【IDEA】DeBug(图文并茂)

文章目录 01_Debug简介和意义02_IDEA中的Debug步骤03_跳转到当前代码执行的行04_步过调试的使用05_步入调试的使用06_强制步入调试的使用07_步出调试的使用08_回退断点调试的使用09_运行到光标处10_计算表达式11_条件断点12_多线程调试 IDAEA&#xff08;Interactive Data Anal…