《C++新经典对象模型》之第6章 对象构造语义学

《C++新经典对象模型》之第6章 对象构造语义

  • 6.1 继承体系下的对象构造
    • 6.1.1 对象的构造顺序
    • 6.1.2 虚函数
    • 6.1.3 构造函数中对虚函数的调用
        • 06.01.cpp
  • 6.2 对象复制语义学与析构函数语义学
    • 6.2.1 对象的默认复制行为
    • 6.2.2 拷贝赋值运算符与拷贝构造函数
    • 6.2.3 禁止对象的拷贝构造和赋值
    • 6.2.4 析构函数语义
        • 06.02.cpp
  • 6.3 局部对象、全局对象的构造和析构
    • 6.3.1 局部对象的构造和析构
    • 6.3.2 全局对象的构造和析构
        • 06.03.cpp
    • 6.4 局部静态对象、对象数组构造析构和内存分配
      • 6.4.1 局部静态对象的构造和析构
      • 6.4.2 局部静态对象数组的内存分配
        • 06.04.cpp
    • 6.5 new、delete运算符与内存高级话题
        • 06.05.cpp
    • 6.6 临时性对象的详细探讨
      • 6.6.1 拷贝构造函数相关的临时性对象
      • 6.6.2 拷贝赋值运算符相关的临时性对象
      • 6.6.3 直接运算产生的临时性对象
        • 06.06.cpp

6.1 继承体系下的对象构造

6.1.1 对象的构造顺序

从父类到子类,从根源到末端。
先成员变量(定义顺序,从上到下),后构造函数(先初始化列表,后函数体)。

析构顺序:与构造顺序相反。
从子类到父类,先析构函数,后成员变量(从下到上)。

C::成员变量1构造函数	//7
C::成员变量2构造函数	//8
C::C()	//9
	B::成员变量1构造函数	//4
	B::成员变量2构造函数	//5
	B::B()	//6
		A::成员变量1构造函数	//1
		A::成员变量2构造函数	//2
		A::A()	//3
			构造函数初始化列表
			构造函数函数体
		
		A::~A()	//7
		A::成员变量2析构函数	//8
		A::成员变量1析构函数	//9
	B::~B()	//4
	B::成员变量2析构函数	//5
	B::成员变量1析构函数	//6
C::~C()	//1
C::成员变量2析构函数	//2
C::成员变量1析构函数	//3

6.1.2 虚函数

给虚函数表指针赋值的语句,编译器会插入到构造函数的函数体之前。

C::C()
	B::B()	
		A::A()
			vptr = A::vftable;	//虚函数表指针赋值
			A构造函数初始化列表
			A构造函数函数体
		vptr = B::vftable;
		B构造函数初始化列表
		B构造函数函数体
	vptr = C::vftable;
	C构造函数初始化列表
	C构造函数函数体

6.1.3 构造函数中对虚函数的调用

(1)构造函数中调用虚函数,并不通过虚函数表来调用,而是直接调用(虚函数有真实地址)。
(2)在构造函数中调用的虚函数从所在类往根类回溯,逐次找这个虚函数,找到哪个就直接调用(静态方式)。
(3)构造函数中调用虚函数时,对象未构造完整,不宜采用虚函数表机制调用虚函数。
(4)不要在类的构造函数和析构函数中调用虚函数。

#include <iostream>
using namespace std;

namespace _n1
{
	class TA
	{
	public:
		TA()
		{
			cout << "TA::TA(), this = " << this << endl;
		}
		~TA()
		{
			cout << "TA::~TA(), this = " << this << endl;
		}
	};

	class TB
	{
	public:
		TB()
		{
			cout << "TB::TB(), this = " << this << endl;
		}
		~TB()
		{
			cout << "TB::~TB(), this = " << this << endl;
		}
	};

	class T1
	{
	public:
		T1()
		{
			cout << "T1::T1(), this = " << this << endl;
		}
		~T1()
		{
			cout << "T1::~T1(), this = " << this << endl;
		}
	};

	class T2 : public T1
	{
	public:
		T2()
		{
			cout << "T2::T2(), this = " << this << endl;
		}
		~T2()
		{
			cout << "T2::~T2(), this = " << this << endl;
		}
	};

	class T3 : public T2
	{
	public:
		T3()
		{
			cout << "T3::T3(), this = " << this << endl;
		}
		~T3()
		{
			cout << "T3::~T3(), this = " << this << endl;
		}

	private:
		TA ta;
		TB tb;
	};
}

namespace _n2
{
	class A
	{
	public:
		A()
		{
			myvirfunc();
			cout << "A::A(), this = " << this << endl;
		}
		virtual ~A()
		{
			myvirfunc();
			cout << "A::~A(), this = " << this << endl;
		}

	public:
		virtual void myvirfunc()
		{
			myvirfunc2();
			cout << "A::myvirfunc(), this = " << this << endl;
		}
		virtual void myvirfunc2()
		{
			cout << "A::myvirfunc2(), this = " << this << endl;
		}
	};

	class B : public A
	{
	public:
		B()
		{
			myvirfunc();
			cout << "B::B(), this = " << this << endl;
		}
		virtual ~B()
		{
			myvirfunc();
			cout << "B::~B(), this = " << this << endl;
		}

	public:
		virtual void myvirfunc()
		{
			myvirfunc2();
			cout << "B::myvirfunc(), this = " << this << endl;
		}
		virtual void myvirfunc2()
		{
			cout << "B::myvirfunc2(), this = " << this << endl;
		}
	};

	class C : public B
	{
	public:
		C() : m_c(11)
		{
			myvirfunc(); // 调用一个虚函数
			cout << "C::C(), this = " << this << endl;
		}
		virtual ~C()
		{
			myvirfunc();
			cout << "C::~C(), this = " << this << endl;
		}

	public:
		int m_c;

	public:
		virtual void myvirfunc()
		{
			myvirfunc2();
			cout << "C::myvirfunc(), this = " << this << endl;
		}
		virtual void myvirfunc2()
		{
			cout << "C::myvirfunc2(), this = " << this << endl;
		}
	};
}

int main()
{
	if (0)
	{
		_n1::T3 t3;
	}

	if (0)
	{
		_n2::C c;
	}

	if (1)
	{
		_n2::A *mycobj = new _n2::C();
		mycobj->myvirfunc();
		delete mycobj;
	}

	return 0;
}
06.01.cpp
#include <cstdio>
#include <iostream>
using namespace std;

struct A
{
	A() { printf("A::A(), this = %p\n", this); }
	virtual ~A() {}

	virtual void myvirfunc2()
	{
		printf("A::myvirfunc2()\n");
	}
	virtual void myvirfunc()
	{
		myvirfunc2();
		printf("A::myvirfunc()\n");
	}
};
struct B : A
{
	B()
	{
		myvirfunc();
		printf("B::B(), this = %p\n", this);
	}
	virtual ~B() {}

	virtual void myvirfunc2()
	{
		printf("B::myvirfunc2()\n");
	}
	virtual void myvirfunc()
	{
		myvirfunc2();
		printf("B::myvirfunc()\n");
	}
};
struct C : B
{
	int m_c;
	C()
		: m_c(11)
	{
		myvirfunc(); // 调用一个虚函数
		printf("C::C(), this = %p\n", this);
	}
	virtual ~C()
	{
		myvirfunc();
	}

	virtual void myvirfunc2()
	{
		printf("C::myvirfunc2()\n");
	}
	virtual void myvirfunc()
	{
		myvirfunc2();
		printf("C::myvirfunc()\n");
	}
};

int main()
{
	// C c;

	C *mycobj = new C();
	// mycobj->myvirfunc();
	delete mycobj;

	cout << "Over!\n";
	return 0;
}

6.2 对象复制语义学与析构函数语义学

6.2.1 对象的默认复制行为

无拷贝构造函数和拷贝赋值运算符时,默认的对象复制行为会发挥作用。

6.2.2 拷贝赋值运算符与拷贝构造函数

提供拷贝赋值运算符或拷贝构造函数时,需提供默认构造函数(此时编译器不会生成)。


struct JI
{
	JI() { cout << "JI::JI()" << endl; }
	virtual ~JI() { cout << "JI::~JI()" << endl; }
};

struct A : JI
{
	int m_i, m_j;
	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }

	A &operator=(const A &tmp)
	{
		if (&tmp == this)
			return *this;

		// static_cast<JI&>(*this) = tmp; // 调用父类的拷贝赋值运算符
		// JI::operator=(tmp);// 调用父类的拷贝赋值运算符

		m_i = tmp.m_i;
		m_j = tmp.m_j;
		cout << "A::operator=(const A&)" << endl;
		return *this;
	}
	A(const A &tmp)
	//: JI(tmp) // 显式调用父类拷贝构造函数
	{
		// 编译器会插入代码,父类按位复制,调用父类的构造函数或者拷贝构造函数?
		m_i = tmp.m_i;
		m_j = tmp.m_j;
		cout << "A::A(const A&)" << endl;
	}
};

6.2.3 禁止对象的拷贝构造和赋值

(1)类中声明私有的拷贝构造函数和拷贝赋值运算符,无函数体。

class A {
	private:
		A& operator=(const A&);
		A(const A&);
};

(2)c++11,=delete,将拷贝构造函数和拷贝赋值运算符标记为禁用。

class A {
	public:
		A& operator=(const A&) =delete;
		A(const A&) =delete;
};

6.2.4 析构函数语义

  1. 析构函数被合成
    编译器合成析构函数的情况:
    (1)类继承的父类具有析构函数,会合成析构函数并调用父类的析构函数。
    (2)类中类类型成员变量具有析构函数,会合成析构函数并调用该成员变量所属类的析构函数。

  2. 析构函数被扩展
    已写析构函数时,向其中增加代码的情况:
    (1)类继承的父类具有析构函数,会在析构函数后面插入代码,调用父类的析构函数。
    (2)类中类类型成员变量具有析构函数,会在析构函数后面插入代码,调用该成员变量所属类的析构函数。

06.02.cpp
#include <iostream>
using namespace std;

struct JI
{
	JI() { cout << "JI::JI()" << endl; }
	virtual ~JI() { cout << "JI::~JI()" << endl; }
};

struct A : JI
{
	int m_i, m_j;

	// private:
	//	A& operator=(const A& tmp);
	//	A(const A& tmptime);

	// public:
	//	A& operator=(const A& tmp) = delete;
	//	A(const A& tmptime) = delete;

	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }

	A &operator=(const A &tmp)
	{
		if (&tmp == this)
			return *this;

		// static_cast<JI&>(*this) = tmp; // 调用父类的拷贝赋值运算符
		// JI::operator=(tmp);// 调用父类的拷贝赋值运算符

		m_i = tmp.m_i;
		m_j = tmp.m_j;
		cout << "A::operator=(const A&)" << endl;
		return *this;
	}
	A(const A &tmp)
	//: JI(tmp) // 显式调用父类拷贝构造函数
	{
		// 编译器会插入代码,调用父类的构造函数或者拷贝构造函数?
		m_i = tmp.m_i;
		m_j = tmp.m_j;
		cout << "A::A(const A&)" << endl;
	}
};

struct ParC
{
	virtual ~ParC() { cout << "ParC::~ParC()" << endl; }
};
struct MemC
{
	ParC m_j;

	~MemC() { cout << "MemC::~MemC()" << endl; }
};

int main()
{
	{
		A aobj;
		aobj.m_i = 15;
		aobj.m_j = 20;

		A aobj2 = aobj; // 执行拷贝构造

		A aobj3;
		aobj3.m_i = 13;
		aobj3.m_j = 16;
		aobj2 = aobj3; // 执行拷贝复制运算符
	}

	{
		MemC mobj;
	}

	cout << "Over!\n";
	return 0;
}

6.3 局部对象、全局对象的构造和析构

6.3.1 局部对象的构造和析构

只要超出了对象的作用域,编译器总会在适当的地方插入调用对象析构函数的代码。
尽量把对象定义在需要立即用到它的代码段的附件。

6.3.2 全局对象的构造和析构

全局对象的初始化和释放过程:
(1)全局对象获得地址(可执行文件中确定的,和堆、栈中分配内存不一样,程序运行期间一直存在)。
(2)全局对象内存清零(静态初始化)。
(3)调用全局对象对应类的构造函数。
(4)执行main函数。
(5)main函数执行完毕后,调用全局对象对应类的析构造函数。
(6)整个可执行程序执行完毕。

06.03.cpp
#include <iostream>
using namespace std;

struct A
{
	int m_i;
	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }
};

void myfunc()
{
	// A obja; // 这里定义不合适
	if (1 == 1)
	{
		// 这里会被编译器插入调用obja对象析构函数的代码,影响执行效率完全没必要
		return;
	}

	A obja; // 这里定义合适
	obja.m_i = 10;
	cout << "obja.m_i = " << obja.m_i << endl;
	return;
}

A g_aobj;

int main()
{
	{
		A obja;
		int mytest = 1;
		if (mytest == 0)
			return 0;
		myfunc();
	}

	g_aobj.m_i = 6; //

	cout << "Over!\n";
	return 0;
}

6.4 局部静态对象、对象数组构造析构和内存分配

6.4.1 局部静态对象的构造和析构

只有当函数调用时,局部静态对象的构造函数才会执行。
多次调用,只有第一次才构造。
编译时内存开始地址和大小已确定(BSS段),运行到对应代码时,才从事先约定好的地址分配出来。
构造实现方式:局部静态对象地址旁增加字节做标记(是否已构造),第一次构造,第二次标记存在,不构造,跳过static A s_aobj;代码行。
析构实现方式:第一次构造时,增加代码_atexit登记信息,main函数执行完后执行析构。

6.4.2 局部静态对象数组的内存分配

同局部静态对象类似。
但编译器优化,大数组不分配实际的物理地址,对象数组做有用事情时,才实际分配。

06.04.cpp
#include <cstdio>
#include <iostream>
using namespace std;

struct A
{
	int m_i;
	// A() { cout << "A::A()" << endl; }
	//~A() { cout << "A::~A()" << endl; }
};

void myfunc1()
{
	static A s_aobj1;
	static A s_aobj2;
	printf("&s_aobj1=%p\n", &s_aobj1);
	printf("&s_aobj2=%p\n", &s_aobj2);
}
const A &myfunc2()
{
	static A s_aobj1;
	printf("&s_aobj1=%p\n", &s_aobj1);
	return s_aobj1;
}

void myfunc()
{
	static A s_aobj[1000'0000]; // '是数字分隔符, C++14
	for (int i = 0; i < 1000'0000; i++)
		s_aobj[i].m_i = i;//大数组做有用事情时,才实际分配内存
	printf("s_aobj = %p\n", s_aobj);
}

int main()
{
	myfunc1();
	myfunc1();

	printf("&A=%p\n", &myfunc2());
	printf("&A=%p\n", &myfunc2());

	myfunc();
	myfunc();

	cout << "Over!\n";
	return 0;
}

6.5 new、delete运算符与内存高级话题

void *p = malloc(0);
char *q = new char[0];

new内部调用malloc,两行代码几乎等价。
代码诡异,可能报错,别使用。

06.05.cpp
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int main()
{
	void *p = malloc(0);
	char *q = new char[0];

	strcpy_s((char *)p, 100, "This is a test1!");
	strcpy_s(q, 100, "This is a test2!");

	printf("p=%s\n", p);
	printf("q=%s\n", q);

	delete[] q;
	free(p);

	cout << "Over!\n";
	return 0;
}

6.6 临时性对象的详细探讨

6.6.1 拷贝构造函数相关的临时性对象

struct A
{
	int m_i;
	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }

	A(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::A(const A&)" << endl;
	}
	A &operator=(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::operaotor=(const A&)" << endl;
		//printf("&tmp=%p\n", &tmp);
		return *this;
	}
};
A operator+(const A &obj1, const A &obj2)
{
	// cout << "A operaotor+(const A&, const A &)" << endl;

	A tmp; // 一次构造函数
	tmp.m_i = obj1.m_i + obj2.m_i;

	printf("&tmp=%p\n", &tmp);
	return tmp; // 一次拷贝构造函数+一次析构函数(编译器优化后可能无临时对象)
}

if (1)
	{
		A myobj1;
		printf("&myobj1=%p\n", &myobj1);

		A myobj2;
		printf("&myobj2=%p\n", &myobj2);

		A resultobj1 = myobj1 + myobj2;//编译器优化后直接将tmp的地址用作resultobj1的地址,节省一次复制构造+一次析构
		cout << "------------------------\n";
		printf("&resultobj1=%p\n", &resultobj1);
	}

6.6.2 拷贝赋值运算符相关的临时性对象

struct A
{
	int m_i;
	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }

	A(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::A(const A&)" << endl;
	}
	A &operator=(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::operaotor=(const A&)" << endl;
		//printf("&tmp=%p\n", &tmp);
		return *this;
	}
};
A operator+(const A &obj1, const A &obj2)
{
	// cout << "A operaotor+(const A&, const A &)" << endl;

	A tmp; // 一次构造函数
	tmp.m_i = obj1.m_i + obj2.m_i;

	printf("&tmp=%p\n", &tmp);
	return tmp; // 一次拷贝构造函数+一次析构函数(编译器优化后可能无临时对象)
}

if (1)
	{
		A myobj1;
		printf("&myobj1=%p\n", &myobj1);

		A myobj2;
		printf("&myobj2=%p\n", &myobj2);

		A resultobj2;
		
		resultobj2 = myobj1 + myobj2;//一次构造函数+一次拷贝赋值运算符+一次析构
		cout << "------------------------\n";
		printf("&resultobj2=%p\n", &resultobj2);
	}

6.6.3 直接运算产生的临时性对象

if (1)
	{
		A myobj1;
		A myobj2;
		myobj1 + myobj2;//一次构造+一次析构
	}
	
if (1)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		printf("(myobj1 + myobj2).m_i = %d\n", (myobj1 + myobj2).m_i); // 临时对象(operator+,一次构造+一次析构,且析构在printf后执行)
	}
	
if (1)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		if ((myobj1 + myobj1).m_i > 3 || (myobj1 + myobj2).m_i > 5) // 两个临时对象
			cout << "Condition established" << endl;
		else
			cout << "Condition not established" << endl;
	}
	
if (1)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		if ((myobj1 + myobj1).m_i > 1 || (myobj1 + myobj2).m_i > 5) // 一个临时对象
			cout << "Condition established" << endl;
	}

编译器会在必要的地方插入代码产生临时对象,完成要实现的意图。

  1. 临时对象被摧毁
if (0)
	{
		const char *p = (string("123") + string("456")).c_str(); // error,临时对象被摧毁了
		printf("p = %s\n", p);

		string aaa = string("123") + string("456");
		const char *q = aaa.c_str(); // ok
		printf("q = %s\n", q);
	}
  1. 临时对象因绑定到引用而被保留
if (0)
	{
		const string &aaa2 = string("123") + string("456");
		printf("aaa2 = %s\n", aaa2.c_str());
	}
06.06.cpp
#include <cstdio>
#include <iostream>
using namespace std;

struct A
{
	int m_i;
	A() { cout << "A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }

	A(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::A(const A&)" << endl;
	}
	A &operator=(const A &tmp)
	{
		m_i = tmp.m_i;
		cout << "A::operaotor=(const A&)" << endl;
		// printf("&tmp=%p\n", &tmp);
		return *this;
	}
};
A operator+(const A &obj1, const A &obj2)
{
	// cout << "A operaotor+(const A&, const A &)" << endl;

	A tmp; // 一次构造函数
	tmp.m_i = obj1.m_i + obj2.m_i;

	printf("&tmp=%p\n", &tmp);
	return tmp; // 一次拷贝构造函数+一次析构函数(编译器优化后可能无临时对象)
}

int main()
{
	if (0)
	{
		A myobj1;
		printf("&myobj1=%p\n", &myobj1);

		A myobj2;
		printf("&myobj2=%p\n", &myobj2);

		A resultobj1 = myobj1 + myobj2; // 编译器优化后直接将tmp的地址用作resultobj1的地址,节省一次复制构造+一次析构
		cout << "------------------------\n";
		printf("&resultobj1=%p\n", &resultobj1);
	}

	if (0)
	{
		A myobj1;
		printf("&myobj1=%p\n", &myobj1);

		A myobj2;
		printf("&myobj2=%p\n", &myobj2);

		A resultobj2;

		resultobj2 = myobj1 + myobj2; // 一次构造函数+一次拷贝赋值运算符+一次析构
		cout << "------------------------\n";
		printf("&resultobj2=%p\n", &resultobj2);
	}
	if (0)
	{
		A myobj1;
		A myobj2;
		myobj1 + myobj2;
	}

	if (0)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		printf("(myobj1 + myobj2).m_i = %d\n", (myobj1 + myobj2).m_i); // 临时对象(operator+,一次构造+一次析构,且析构在printf后执行)
	}
	if (0)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		if ((myobj1 + myobj1).m_i > 3 || (myobj1 + myobj2).m_i > 5) // 两个临时对象
			cout << "Condition established" << endl;
		else
			cout << "Condition not established" << endl;
	}
	if (1)
	{
		A myobj1;
		myobj1.m_i = 1;

		A myobj2;
		myobj2.m_i = 2;

		if ((myobj1 + myobj1).m_i > 1 || (myobj1 + myobj2).m_i > 5) // 一个临时对象
			cout << "Condition established" << endl;
	}

	if (0)
	{
		const char *p = (string("123") + string("456")).c_str(); // error,临时对象被摧毁了
		printf("p = %s\n", p);

		string aaa = string("123") + string("456");
		const char *q = aaa.c_str(); // ok
		printf("q = %s\n", q);
	}

	if (0)
	{
		const string &aaa2 = string("123") + string("456");
		printf("aaa2 = %s\n", aaa2.c_str());
	}

	cout << "Over!\n";
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI系统性学习05—向量数据库

文章目录 1、Chroma向量数据库1.1 安装Chroma1.2 初始化Chroma客户端1.3 创建一个合集1.4 添加数据1.5 查询数据1.6 持久化数据1.7 集合操作1.7.1 创建集合1.7.2 获取集合1.7.3 删除集合1.7.4 其他操作1.8 向集合添加数据1.9 查询集合数据1.10 更新集合数据1.11 删除集合数据1.…

[CISCN2019 华北赛区 Day1 Web5]CyberPunk --不会编程的崽

继续sql,哈哈。我按照我的思路来讲。 四个功能&#xff0c;提交&#xff0c;查找&#xff0c;修改&#xff0c;删除。多半是sql注入&#xff0c;而且又有修改&#xff0c;查找功能。多半还是二次注入。昨天那个修改密码的&#xff0c;也是二次注入。这里需要先找到注入点 姓名 …

STM32CubeMX学习笔记24---FreeRTOS(消息队列)

一. 队列简介 队列是为了任务与任务、任务与中断之间的通信而准备的&#xff0c;可以在任务与任务、任务与中 断之间传递消息&#xff0c;队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之 间要交流的数据保存在队列中&#xff0c;叫做队列项目。队列…

SLAM 求解IPC算法

基础知识&#xff1a;方差&#xff0c;协方差&#xff0c;协方差矩阵 方差&#xff1a;描述了一组随机变量的离散程度 方差 每个样本值 与 全部样本的平均值 相差的平方和 再求平均数&#xff0c;记作&#xff1a; 例如&#xff1a;计算数字1-5的方差&#xff0c;如下 去中心化…

Vulnhub靶机渗透:DC-7打靶记录

前言 自信自强&#xff0c;来自于不怕苦、不怕难的积淀。宝剑锋从磨砺出&#xff0c;梅花香自苦寒来&#xff1b;任何美好理想&#xff0c;都离不开筚路蓝缕、手胼足胝的艰苦奋斗&#xff01; 靶场介绍 DC-7是一个初中级的靶场&#xff0c;需要具备以下前置知识&#xff1a;…

【开源】SpringBoot框架开发不良邮件过滤系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统用户模块2.2 收件箱模块2.3 发件箱模块2.4 垃圾箱模块2.5 回收站模块2.6 邮箱过滤设置模块 三、实体类设计3.1 系统用户3.2 邮件3.3 其他实体 四、系统展示五、核心代码5.1 查询收件箱档案5.2 查询回收站档案5.3 新…

[Qt项目实战]Qt实现美松标签打印机标签二维码打印(QR混排模式+页打印模式)

1、硬件信息、环境参数及配套资料 1.1 打印机信息及开发环境 打印机 美松标签打印机串口/USB通讯Qt5.9 64位程序 1.2 打印机配套开发资料 打印机主要配套测试工具、开发SDK及驱动等&#xff0c;均由厂家提供。 开发Demo及动态库&#xff1a;MsPrintSDK-DLL-V2.2.2.5 链接&…

3、java虚拟机-类的生命周期-初始化阶段(与程序员有关)

一 、静态代码块执行顺序和字节码文件中的执行顺序以及什么赋值。 类的生命周期-初始化阶段-被static所修饰的常量才会被赋予值 初始化阶段-代码中静态代码块和静态变量的顺序和字节码中的执行顺序是一致的。 二、4种情况下&#xff0c;类会被初始化。 1、怎样查看类是…

阿里云部署MySQL、Redis、RocketMQ、Nacos集群

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容MySQL集群配置云服务器选购CPU选择内存选择云盘选择ESSD AutoPL云盘块存储性能&#xff08;ESSD&#xff09; 镜像选择带宽选择密码配置注意事项 搭建宝塔面板方便管理云服务器云服务器的安全组安装docker和docker-compose…

Go语言学习13-常见软件架构的实现

Go语言学习13-常见软件架构的实现 架构模式 An architectural pattern is a general, reusable solution to a commonly occurring problem in software architectural within a given context. ——wikipedia Pipe-Filter 架构 Pipe-Filter 模式 非常适合于数据处理及数据分…

taro之Picker,PickerView基础用法

1.Picker 直接上代码 import Taro,{Component} from "tarojs/taro"; import {View,Picker} from tarojs/components import { AtIcon } from taro-ui import { putKey } from /src/utils/storage-utilsclass AgriculturePolicy extends Component{constructor (prop…

su: authentication failure 解决方法

产生问题的原因&#xff1a;使用su和sudo是有区别的&#xff0c;root的密码可能是原始的密码&#xff0c;你现在用户的密码大概率是更改过的 解决办法&#xff1a; 在Linux上切换root时&#xff0c;密码正确。。但提示&#xff1a;su: authentication failure ->sudo pa…

统计学基础概念和在AI中的应用

基本概念 统计学是一门研究数据收集、分析、解释和展示的科学&#xff0c;它提供了一套方法论&#xff0c;用于理解数据并从数据中得出结论。统计学在各个领域都有应用&#xff0c;包括经济学、医学、工程学、社会科学等。以下是统计学的一些基本概念&#xff1a; 描述性统计…

wireshark数据捕获实验简述

Wireshark是一款开源的网络协议分析工具&#xff0c;它可以用于捕获和分析网络数据包。是一款很受欢迎的“网络显微镜”。 实验拓扑图&#xff1a; 实验基础配置&#xff1a; 服务器&#xff1a; ip:172.16.1.88 mask:255.255.255.0 r1: sys sysname r1 undo info enable in…

B站python爬虫课程笔记(Q16-)

下面是学习的网址&#xff1a; ​​​​​​【Python爬虫】 16、捕捉异常try&except语句的一些问题 1&#xff09;一些常见的异常类型 IndexError索引错误ZeroDivisionError除零错误FileNotFindError找不到文件错误TypeError类型错误KeyError键错误ValueError值错误Ind…

代理IP品质对Tik Tok代理的重要性

随着Tik Tok的迅速崛起&#xff0c;越来越多的人开始关注如何透过Tik Tok进行行销和推广。其中&#xff0c;使用Tik Tok代理程式是常见的方法。 然而&#xff0c;在选择和使用代理时&#xff0c;IP品质是一个不可忽视的因素。本文将探讨IP品质对Tik Tok代理的重要性&#xff0…

【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024)

【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024) 大会主题: (主题包括但不限于, 更多主题请咨询会务组苏老师) 区块链&#xff1a; 区块链技术和系统 分布式一致性算法和协议 块链性能 信息储存系统 区块链可扩展性 区块…

Springboot笔记-04

1.PropertySource&ImportResource&Bean PropertySource&#xff1a;加载指定的配置文件&#xff0c;只能用于properties文件&#xff0c;不支持yml文件&#xff1b; 以person为例子: ConfigurationProperties:告诉springboot将本类中所有属性和配制文件相关的配制进行…

docker入门(一)—— docker概述

docker 概述 docker 官网&#xff1a;http://www.docker.com 官网文档&#xff1a; https://docs.docker.com/get-docker/ Docker Hub官网&#xff1a;https://hub.docker.com &#xff08;仓库&#xff09; 什么是 docker docker 是一个开源的容器化平台&#xff0c;可以…

ARM Cortex-R82处理器在压缩SSD场景的应用

ScaleFlux公司宣布在其下一代企业级SSD控制器产品线中采用Arm公司的Cortex-R82处理器。这一决策旨在应对企业环境中对高带宽存储解决方案日益增长的需求&#xff0c;并通过提升数据传输速度和效率来满足市场期待。 Arm Cortex-R82处理器是Arm公司迄今为止性能最强的实时处理器…
最新文章