当前位置: 首页 > news >正文

C++(21):特殊工具与技术

控制内存分配

某些应用程序对内存分配有特殊需求,无法直接应用标准内存管理机制。需要自定义内存分配的细节。

重载 new 和 delete

void* operator new(std::size_t size) {// 自定义内存分配逻辑void* ptr = std::malloc(size);if (!ptr) {throw std::bad_alloc(); // 内存分配失败时抛出异常}return ptr;
}

上述代码中,重载了new运算符,std::size_t size参数表示要分配的字节数。在这个函数中,可以实现自己的内存分配逻辑,例如使用malloc来分配内存。如果分配失败,通常会抛出std::bad_alloc异常。

void operator delete(void* ptr) noexcept {// 自定义内存释放逻辑std::free(ptr);
}

重载了delete运算符,接受一个指向要释放内存的指针。在这个函数中,可以实现自己的内存释放逻辑,例如使用free来释放内存。

mallocfree 函数定义在头文件<cstdlib>中。

定位 new 表达式

定位 new 允许在已分配的内存块上创建对象,而不是使用默认的内存分配方式。通常,它用于在已分配的内存上构造对象,例如在内存池或特定内存区域中。

new (place_address) type 
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] { braced initializer list }

其中 place_address必须是一个指针,同时在initializers中提供一个(可能为空的)以逗号分隔的初始值列表,该初始值列表将用于构造新分配的对象。

当只传入一个指针类型的实参时,定位new表达式构造对象但是不分配内存。

#include <iostream>class MyClass {
public:MyClass(int val) : value(val) {}void Print() {std::cout << "Value: " << value << std::endl;}
private:int value;
};int main() {// 分配一块内存用于存储 MyClass 对象void* memory = ::operator new(sizeof(MyClass));// 使用定位 new 表达式在已分配的内存上创建对象MyClass* obj = new (memory) MyClass(42);// 访问对象的方法obj->Print();// 使用定位 delete 表达式释放对象obj->~MyClass();// 释放内存块::operator delete(memory);return 0;
}

运行时类型识别

运行时类型识别(run-time type identification,RTTI)的功能由两个运算符实现:
typeid运算符,用于返回表达式的类型。
dynamic_cast运算符,用于将基类的指针或引用安全地转换成派生类的指针或引用。
当我们将这两个运算符用于某种类型的指针或引用,并且该类型含有虚函数时,运算符将使用指针或引用所绑定对象的动态类型。

dynamic_cast 运算符

dynamic_cast运算符的使用形式如下所示:

dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)

其中,type必须是一个类类型,并且通常情况下该类型应该含有虚函数。在第一种形式中,e必须是一个有效的指针;在第二种形式中,e必须是一个左值;在第三种形式中,e不能是左值。

dynamic_cast 运算符用于在继承层次结构中进行安全的向下转型。它可以将基类指针或引用转换为派生类指针或引用,并且在类型不匹配时返回 nullptr(对指针)或引发 std::bad_cast 异常(对引用)。

#include <iostream>class Base {
public:virtual ~Base() {}
};class Derived : public Base {
public:void DerivedFunction() {std::cout << "DerivedFunction called." << std::endl;}
};int main() {Base* basePtr = new Derived;Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);if (derivedPtr) {derivedPtr->DerivedFunction();} else {std::cout << "无法进行向下转型。" << std::endl;}delete basePtr;return 0;
}//dynamic_cast 运算符用于将 basePtr 转换为 Derived* 类型的指针,如果转换成功,就可以调用 Derived 类的成员函数。

typeid 运算符

typeid表达式的形式是typeid(e),其中e可以是任意表达式或类型的名字。
typeid操作的结果是一个常量对象的引用,该对象的类型是标准库类型type_info或者type_info的公有派生类型。

typeid运算符可以作用于任意类型的表达式。

当运算对象不属于类类型或者是一个不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。而当运算对象是定义了至少一个虚函数的类的左值时,typeid的结果直到运行时才会求得。

typeid 运算符允许获取对象的实际类型信息,以便在运行时判断对象的类型。它通常与 std::type_info 类一起使用。

#include <iostream>
#include <typeinfo>class Base {
public:virtual ~Base() {}
};class Derived : public Base {
};int main() {Base* basePtr = new Derived;if (typeid(*basePtr) == typeid(Derived)) {std::cout << "basePtr指向的对象是Derived类型" << std::endl;} else if (typeid(*basePtr) == typeid(Base)) {std::cout << "basePtr指向的对象是Base类型" << std::endl;}delete basePtr;return 0;
}

使用 RTTI

运行时类型识别(RTTI)可以用来获取对象的实际类型信息,并在运行时根据对象的类型进行不同的处理。

#include <iostream>
#include <typeinfo>class Animal {
public:virtual void Speak() const {std::cout << "Animal speaks." << std::endl;}
};class Dog : public Animal {
public:void Speak() const override {std::cout << "Dog barks." << std::endl;}
};class Cat : public Animal {
public:void Speak() const override {std::cout << "Cat meows." << std::endl;}
};int main() {Animal* animals[3];animals[0] = new Dog();animals[1] = new Cat();animals[2] = new Animal();for (int i = 0; i < 3; i++) {const std::type_info& typeInfo = typeid(*animals[i]);if (typeInfo == typeid(Dog)) {std::cout << "This is a Dog. ";} else if (typeInfo == typeid(Cat)) {std::cout << "This is a Cat. ";} else {std::cout << "This is an unknown animal. ";}animals[i]->Speak();}for (int i = 0; i < 3; i++) {delete animals[i];}return 0;
}//在上述示例中,我们创建了一个基类Animal和两个派生类Dog和Cat。然后,我们创建了一个包含不同类型的Animal指针的数组,
//并使用typeid运算符获取对象的类型信息。最后,我们根据类型信息执行不同的操作。//要注意的是,typeid返回的是std::type_info类型,可以用来进行类型比较。
//另外,为了使类型比较有效,Animal类中的Speak函数被声明为虚函数,以启用多态性。

尽管RTTI可以实现运行时类型检查和操作,但通常应该避免使用它,因为它可能会引入性能开销,并且有时更好的做法是使用多态性和虚函数来避免需要RTTI。 RTTI通常在某些特定的情况下才会用到。

type_info 类

type_info类的精确定义随着编译器的不同而略有差异。
它用于表示类型信息。std::type_info 的对象通常由运行时类型识别(RTTI)操作 typeid 返回。它包含有关一个类型的信息,例如类型的名称或类型的标识。

1.获取类型信息:std::type_info 通常通过 typeid 运算符来获取类型信息。

const std::type_info& typeInfo = typeid(MyClass);

2.比较类型信息:可以使用 std::type_info 对象来比较类型信息。

const std::type_info& typeInfo1 = typeid(MyClass1);
const std::type_info& typeInfo2 = typeid(MyClass2);if (typeInfo1 == typeInfo2) {// 类型相同
} else {// 类型不同
}

3.获取类型名称:std::type_info 对象可以用于获取类型的名称。

const std::type_info& typeInfo = typeid(MyClass);
std::cout << "Type name: " << typeInfo.name() << std::endl;

4.比较类型信息的安全性:std::type_info 对象的比较是类型安全的,即使在继承层次结构中也可以正常工作。这对于执行基于多态性的类型检查非常有用。

Base* basePtr = new Derived;
const std::type_info& baseTypeInfo = typeid(Base);
const std::type_info& derivedTypeInfo = typeid(*basePtr);if (derivedTypeInfo == baseTypeInfo) {// 此代码块不会执行,因为类型不同
}

枚举类型

枚举类型可以将一组整型常量组织在一起。和类一样,每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。

C++包含两种枚举:限定作用域的和不限定作用域的。

定义限定作用域的枚举类型:首先关键字 enum class(或等价使用 enum struct),随后是枚举类型名字以及用花括号括起来以逗号分隔的枚举成员列表,最后是一个分号:enum class open_modes {input,output,append) ;

定义不限定作用域的枚举类型:省略掉关键字 class(或 struct),枚举类型的名字是可选的:

enum color{red, yellow, green};	//不限定作用域的枚举类型
//未命名的、不限定作用域的枚举类型
enum (floatPrec = 6, doublePrec = 10,double_doublePrec = 10);

枚举成员

在限定作用域的枚举类型中,枚举成员的名字遵循常规的作用域准则,并且在枚举类型的作用域外是不可访问的。
与之相反,在不限定作用域的枚举类型中,枚举成员的作用域与枚举类型本身的作用域相同。

默认情况下,枚举值从0开始,依次加1。不过也能为一个或几个枚举成员指定专门的值。

枚举成员是const,因此在初始化枚举成员时提供的初始值必须是常量表达式。
可以定义枚举类型的constexpr变量:

constexpr intTypes charbits = intTypes::charTyp;

switch 语句中 case 标签的值必须是常量表达式,可以用枚举成员做 case 标签。

和类一样,枚举也定义新的类型

只要enum有名字,我们就能定义并初始化该类型的成员。要想初始化enum对象或者为enum对象赋值,必须使用该类型的一个枚举成员或者该类型的另一个对象。

一个不限定作用域的枚举类型的对象或枚举成员自动地转换成整型。

枚举类型的前置声明

可以提前声明 enum,但是不限定作用域的枚举类型在声明时必须指定成员类型。
enum 的声明和定义的成员类型必须匹配。

enum class Color1;    // 前置声明 Color1,成员类型默认为 int
enum uid:long long;   // 前置声明 uid,必须指定成员类型

类成员指针

成员指针是指可以指向类的非静态成员的指针。一般情况下,指针指向一个对象,但是成员指针指示的是类的成员,而非类的对象。
类的静态成员不属于任何对象,因此无须特殊的指向静态成员的指针,指向静态成员的指针与普通指针没有什么区别。

数据成员指针

class MyClass {
public:int data;void Print() {std::cout << "Data: " << data << std::endl;}
};int main() {int MyClass::*memberPtr = &MyClass::data;MyClass obj;obj.*memberPtr = 42;std::cout << obj.data << std::endl;return 0;
}//`定义了一个指向 MyClass 类的 data 数据成员的类成员指针 int MyClass::*memberPtr。
//然后,创建了一个 MyClass 对象 obj,并使用类成员指针来访问和修改 data 数据成员。`

成员函数指针

class MyClass {
public:void PrintHello() {std::cout << "Hello from MyClass" << std::endl;}
};int main() {void (MyClass::*memberFunctionPtr)() = &MyClass::PrintHello;MyClass obj;(obj.*memberFunctionPtr)();return 0;
}//定义了一个指向 MyClass 类的 PrintHello 成员函数的类成员指针 void (MyClass::*memberFunctionPtr)()。
//然后,创建了一个 MyClass 对象 obj,并使用类成员指针来调用 PrintHello 成员函数。

类成员指针的语法可以看起来比较复杂,但它们提供了一种强大的机制,用于在运行时动态选择要调用的成员函数或访问的数据成员,这在某些高级编程场景中非常有用。需要注意的是,类成员指针的类型与要指向的成员的类型有关,因此需要确保它们的类型匹配。

嵌套类

一个类可以定义在另一个类的内部,称之为嵌套类。
嵌套类可以访问外部类的私有成员,并且通常用于实现一种封装和组织的方式,以将相关联的类放在一起。嵌套类的一个常见用途是作为外部类的辅助类,用于实现某些功能。

嵌套类是一个独立的类,与外层类基本没什么关系。特别是,外层类的对象和嵌套类的对象是相互独立的。在嵌套类的对象中不包含任何外层类定义的成员:类似的,在外层类的对象中也不包含任何嵌套类定义的成员。

嵌套类的名字在外层类作用域中是可见的,在外层类作用域之外不可见。和其他嵌套的名字一样,嵌套类的名字不会和别的作用域中的同一个名字冲突。

#include <iostream>class OuterClass {
public:// 外部类的构造函数OuterClass(int value) : data(value) {}// 嵌套类的定义class NestedClass {public:NestedClass(int nestedValue) : nestedData(nestedValue) {}void Display() {std::cout << "Nested Data: " << nestedData << std::endl;}private:int nestedData;};void AccessNestedClass() {NestedClass nested(42);nested.Display();}private:int data;
};int main() {OuterClass outer(10);outer.AccessNestedClass();return 0;
}//定义了一个外部类 OuterClass 和一个嵌套类 NestedClass。嵌套类 NestedClass 可以访问外部类 OuterClass 的私有成员,例如 data。在 AccessNestedClass 成员函数中,创建了一个 NestedClass 的对象并访问了它的成员函数。

在嵌套类在其外层类之外完成真正的定义之前,它都是一个不完全类型.

union:一种节省空间的类

联合(union)是一种特殊的类。一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。
1.union 不能含有引用类型的成员
2.union 不能含有虚函数。因为,union既不能继承自其他类,也不能作为基类使用。
在C++11新标准中,含有构造函数或析构函数的类类型也可以作为union的成员类型。union可以为其成员指定publicprotectedprivate等保护标记。默认情况下,union 的成员都是公有的,与struct相同。

定义 union 类型

union 用来定义一组类型不同的互斥值。

// MyUnion 类型的对象只有一个成员,该成员的类型可能是下列类型中的任意一种
union MyUnion {int intValue;float floatValue;char charValue;
};

使用 union 类型

默认情况下union 是未初始化的。
可以使用花括号内的初始值来初始化一个 union,像聚合类一样。
提供的初始值被用于初始化 union 的第一个成员。

使用通用的成员访问运算符来访问一个 union 对象的成员。
union 的一个数据成员赋值会令其他成员变为未定义状态。

MyUnion u;
u.intValue = 42;std::cout << "intValue: " << u.intValue << std::endl;
std::cout << "floatValue: " << u.floatValue << std::endl; // 未定义行为
std::cout << "charValue: " << u.charValue << std::endl;   // 未定义行为

匿名 union

匿名union 是一个未命名的union,并且在右花括号和分号之间没有任何声明。
在匿名union的定义所在的作用域内该union的成员都是可以直接访问的,因此匿名union不能包含受保护的成员或私有成员,也不能定义成员函数。

含有类类型成员的 union

C++ 允许在联合(union)中包含类类型的成员。这样的联合可以存储不同类类型的对象,但要注意管理它们的生命周期以避免资源泄漏。

#include <iostream>class A {
public:A(int value) : data(value) {}void Print() {std::cout << "A: " << data << std::endl;}
private:int data;
};class B {
public:B(float value) : data(value) {}void Print() {std::cout << "B: " << data << std::endl;}
private:float data;
};union MyUnion {A aObj;B bObj;
};int main() {MyUnion u;u.aObj = A(42);u.aObj.Print();u.bObj = B(3.14f);u.bObj.Print();// 在联合中切换到不同的类对象类型u.aObj = A(100);u.aObj.Print();return 0;
}//定义了两个类 A 和 B,它们分别具有不同的数据成员和成员函数。
//然后,创建了一个联合 MyUnion,它包含两个类类型的成员 aObj 和 bObj。//首先将 aObj 设置为 A 类对象,并调用其 Print 成员函数来显示数据。
//然后将 bObj 设置为 B 类对象,并调用其 Print 成员函数。
//请注意,每次切换联合的成员对象时,上一个成员对象的析构函数会被调用,因此需要小心管理对象的生命周期。

使用类类型的成员的联合通常要更加谨慎,因为需要确保在切换成员对象时正确管理资源和对象的状态。联合通常用于特定的底层编程场景,如需要在不同的类对象之间共享内存或进行位操作时。在一般情况下,最好使用结构体或类来更安全地组织数据。

局部类

定义在某个函数内部的类称为局部类。局部类定义的类型只在定义它的作用域内可见。

局部类的所有成员(包括函数在内)都必须完整定义在类的内部。在局部类中也不允许声明静态数据成员。

局部类不能使用函数作用域中的变量。
局部类只能访问外层作用域定义的类型名、静态变量以及枚举成员。如果局部类定义在某个函数内部,则该函数的普通局部变量不能被该局部类使用。

外层函数对局部类的私有成员没有任何访问特权。当然,局部类可以将外层函数声明为友元;或者更常见的情况是局部类将其成员声明成公有的。

嵌套的局部类
局部类中可以再嵌套一个类,此时嵌套类的定义可以出现在局部类之外,不过必须定义在与局部类相同的作用域中。
局部类中的嵌套类也是一个局部类,必须遵循局部类的各种规定。

固有的不可移植的特性

不可移植的特性是指因机器而异的特性。

位域

类可以将其(非静态)数据成员定义成位域,在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。
位域在内存中的布局是与机器相关的。

位域的类型必须是整型或枚举类型。因为带符号位域的行为是由具体实现确定的,所以在通常情况下我们使用无符号类型保存一个位域。
位域的声明形式是在成员名字之后紧跟一个冒号以及一个常量表达式,该表达式用于指定成员所占的二进制位数:

#include <iostream>struct Flags {unsigned int flag1 : 1;  // 1位unsigned int flag2 : 2;  // 2位unsigned int flag3 : 3;  // 3位
};int main() {Flags flags;flags.flag1 = 1;   // 设置第1位为1flags.flag2 = 2;   // 设置第2、3位为10flags.flag3 = 5;   // 设置第4、5、6位为101std::cout << "flag1: " << flags.flag1 << std::endl;std::cout << "flag2: " << flags.flag2 << std::endl;std::cout << "flag3: " << flags.flag3 << std::endl;return 0;
}//定义了一个结构体 Flags,它包含了三个位域成员 flag1、flag2 和 flag3,它们分别具有1位、2位和3位。
//可以使用赋值操作符来设置每个位域的值,并使用位域名称来访问它们。
  1. 位域成员的位数通常不能超过基础整数类型(如 int)的位数。例如,在大多数平台上,int 是32位,所以位域成员的位数应小于或等于32。
  2. 位域成员的符号(有符号或无符号)由编译器决定。如果你需要确切的符号性,可以使用 signed 或 unsigned 修饰符。
  3. 不同编译器的位域布局规则可能不同,所以在跨平台开发时要小心使用位域。
  4. 位域的可移植性较差,因为它们受限于底层硬件和编译器的实现。如果需要高度可移植的代码,最好避免使用位域。
  5. 位域通常用于表示标志或状态位,以节省内存。但要小心位域的复杂性和可移植性,确保在需要时使用位域。
  6. 取地址运算符(&)不能作用于位域,因此任何指针都无法指向类的位域。

volatile 限定符

关键字 volatile 告诉编译器不要对它所修饰的对象进行优化。

volatile int display_register;	//该int值可能发生改变
volatile Task *curr_task;		//curr_task指向一个volatile对象
volatile int iax[max_size];		//iax的每个元素都是volatile
volatile screen bitmapBuf;		//bitmapBuf的每个成员都是volatile

constvolatile限定符互相没什么影响,某种类型可能既是 const的也是volatile的,此时它同时具有二者的属性。
也可以将成员函数定义为 volatile,只有 volatile 的成员函数才能被 volatile 的对象调用。

可以声明volatile指针、指向volatile对象的指针以及指向volatile对象的volatile指针:

volatile int v;		//v是一个volatile int
int *volatile vip;		//vip是一个volatile指针,它指向int
volatile int *ivp;		//ivp是一个指针,它指向一个volatile int
volatile int *volatile vivp; //vivp是一个volatile指针,它指向一个volatile intint *ip = &v;

合成的拷贝对 volatile 对象无效

不能使用合成的拷贝/移动构造函数及赋值运算符初始化 volatile 对象或从 volatile 对象赋值。
因为合成的成员接受的形参类型是(非volatile)常量引用,不能把一个非 volatile引用绑定到一个volatile对象上。

如果一个类希望拷贝、移动或赋值它的 volatile对象,则该类必须自定义拷贝或移动操作。

class Foo {
public:Foo(const volatile Foo&);						// 从一个 volatile 对象进行拷贝Foo& operator=(const volatile Foo&);			// 将一个 volatile 对象赋值给一个非 volatile 对象Foo& operator=(const volatile Foo&) volatile;	// 将一个 volatile 对象赋值给一个 volatile 对象
}

链接指示:extern “C”

其他语言中的函数名字也必须在C++中进行声明,并且该声明必须指定返回类型和形参列表。对于其他语言编写的函数来说,编译器检查其调用的方式与处理普通C++函数的方式相同,但是生成的代码有所区别。
C++使用链接指示指出任意非C++函数所用的语言。

要想把C++代码和其他语言(包括C语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器是兼容的。

声明非 C++ 函数

链接指示可以有两种形式:单个的或复合的。链接指示不能出现在类定义或函数定义的内部。同样的链接指示必须在函数的每个声明中都出现。

//可能出现在C++头文件<cstring>中的链接指示
//单语句链接指示
extern "C" size_t strlen (const char *);
//复合语句链接指示
extern "C"{int strcmp (const char*, const char*);char *strcat (char* , const char*);
}

链接指示的第一种形式包含一个关键字extern,后面是一个字符串字面值常量以及一个“普通的”函数声明。

链接指示与头文件

可以令链接指示后面跟上花括号括起来的若干函数的声明,从而一次性建立多个链接。花括号的作用是将适用于该链接指示的多个声明聚合在一起,否则花括号就会被忽略,花括号中声明的函数名字就是可见的,就好像在花括号之外声明的一样。
多重声明的形式可以应用于整个头文件。

//复合语句链接指示
extern "C"{#include <string.h>	//操作C风格字符串的C函数
}

当一个#include指示被放置在复合链接指示的花括号中时,头文件中的所有普通函数声明都被认为是由链接指示的语言编写的。链接指示可以嵌套,因此如果头文件包含带自带链接指示的函数,则该函数的链接不受影响。

重要术语

匿名union 未命名的union,不能用于定义对象。匿名union的成员也是外层作用域的成员。匿名union不能包含成员函数,也不能包含私有成员或受保护的成员。

位域 特殊的类成员,该成员含有一个整型值以指定为其分配的二进制位数。如果可能的话,在类中连续定义的位域将被压缩在一个普通的整数值当中。

dynamic_cast 是一个运算符,执行从基类向派生类的带检查的强制类型转换。当基类中至少含有一个虚函数时,该运算符负责检查指针或引用所绑定的对象的动态类型。如果对象类型与目标类型(或其派生类)一致,则类型转换完成。否则,指针转换将返回一个值为0的指针;引用转换将抛出一个异常。

链接指示 支持C++程序调用其他语言编写的函数的一种机制。所有编译器都应支持调用C++和C函数,至于是否支持其他语言则由编译器决定。

不可移植 固有的与机器有关的特性,当程序转移到其他机器或编译器上时需要修改代码。

相关文章:

C++(21):特殊工具与技术

控制内存分配 某些应用程序对内存分配有特殊需求&#xff0c;无法直接应用标准内存管理机制。需要自定义内存分配的细节。 重载 new 和 delete void* operator new(std::size_t size) {// 自定义内存分配逻辑void* ptr std::malloc(size);if (!ptr) {throw std::bad_alloc(…...

go读取yaml,json,ini等配置文件

实际项目中&#xff0c;要读取一些json等配置文件。今天就来说一说&#xff0c;Golang 是如何读取YAML,JSON,INI等配置文件的。 一. go读取json配置文件 JSON 应该比较熟悉&#xff0c;它是一种轻量级的数据交换格式。层次结构简洁清晰 &#xff0c;易于阅读和编写&#xff0…...

一、安装GoLang环境和开发工具

一、安装GoLang环境 GoLang中国镜像站 下载后对应的环境包以后&#xff0c;一路下一步就好了&#xff0c;安装路径的话&#xff0c;尽量就安装到默认的文件目录下。 二、配置Go的环境变量 右击此电脑–>属性–>高级系统设置–>环境变量&#xff0c;打开环境变量设置…...

条款40:对并发使用std::atomic,对特种内存使用valatile

可怜的volatile。被误解到如此地步。它甚至不应该出现在本章中,因为它与并发程序设计毫无关系。但是在其他程序设计语言中(Java和C#),它还是会对并发程序设计有些用处。甚至在C++中,一些编译器也已经把volatile投入到染缸,使得它的语义显得可以用于并发软件中(但是仅可用…...

Navicat使用HTTP通道服务器进行连接mysql数据库(超简单三分钟完成),centos安装nginx和php,docker安装nginx+php合并版

序言 因为数据库服务器在外网是不能直接连接访问的&#xff0c;但是可以访问网站&#xff0c;网站后台就能访问数据库&#xff0c;所以在此之前&#xff0c;访问数据库的数据是一件非常麻烦的事情&#xff0c;在平时和运维的交流中发现&#xff0c;他们会使用ssh通道进行连接访…...

图:有向无环图(DAG)

1.有向无环图的定义 有向无环图:若一个有向图中不存在环&#xff0c;则称为有向无环图。 简称DAG图(Directed Acyclic Graph) 顶点中不可能出现重复的操作数。 2.有向无环图的应用 1.描述算数表达式 用有向无环图描述算术表达式。 解题步骤&#xff1a; 把各个操作数不重…...

Python入门教程 - 基本语法 (一)

目录 一、注释 二、Python的六种数据类型 三、字符串、数字 控制台输出练习 四、变量及基本运算 五、type()语句查看数据的类型 六、字符串的3种不同定义方式 七、数据类型之间的转换 八、标识符命名规则规范 九、算数运算符 十、赋值运算符 十一、字符串扩展 11.1…...

使用PAM保障开发运营安全

硬编码凭据和 DevOps 系统中缺乏凭据安全性是组织的巨大漏洞。以明文形式访问凭据的恶意内部人员可以在 IT 中建立和扩展其立足点 基础设施&#xff0c;构成巨大的数据被盗风险。 什么是PAM 特权访问管理 &#xff08;PAM&#xff09; 是指一组 IT 安全管理原则&#xff0c;可…...

《Go 语言第一课》课程学习笔记(十二)

函数 Go 函数与函数声明 在 Go 语言中&#xff0c;函数是唯一一种基于特定输入&#xff0c;实现特定任务并可返回任务执行结果的代码块&#xff08;Go 语言中的方法本质上也是函数&#xff09;。在 Go 中&#xff0c;我们定义一个函数的最常用方式就是使用函数声明。 第一部…...

【深入浅出C#】章节10: 最佳实践和性能优化:编码规范和代码风格

编码规范和代码风格之所以重要&#xff0c;是因为它们直接影响到软件开发的质量、可维护性、可读性和协作效率。编码规范和代码风格是编程中的关键要素&#xff0c;它们有助于编写高质量、可维护和易读的代码&#xff0c;提高团队协作效率&#xff0c;减少错误&#xff0c;降低…...

LNMP架构:搭建Discuz论坛

文章目录 1. 编译安装Nginx1.1 前置准备1.2 编译安装1.3 添加nginx系统服务 2.编译安装MySql2.1 前置准备2.2 编译安装2.3 修改mysql 配置文件2.4 设置路径环境变量2.5 初始化数据库2.6 添加musql系统服务2.7 修改MySql登录密码 3. 编译安装PHP3.1 前置准备3.2 编译安装3.3 复制…...

详解Numpy(基于jupyter notebook)

详解Numpy&#xff08;基于jupyter notebook&#xff09; 1.创建数组2.数据类型3.数组切片和索引4.Numpy的广播与数组操作5.数组合并与通用函数6.其他通用函数 1.创建数组 #引入numpy包&#xff0c;以后np就代表numpy import numpy as npanp.arange(10,30,2)#10为起点&#xf…...

nvm集合node版本,解决新版本jeecgboot3.5.3前端启动失败问题

jeecgboot前端3.5.3页面如下 使用之前的pnpm启动会报错&#xff0c;pnpm是node进行安装的&#xff0c;查询后发现&#xff0c;vue3版本的页面至少需要node16版本&#xff0c;我之前的版本只有15.5&#xff0c;适用于vue2 那么我将先前的node15.5版本删除&#xff0c;然后安装…...

Windows命令行初步:更改配色、提示符以及编码方式

文章目录 启动和退出窗口标题和提示符命令行颜色更改编码 启动和退出 按下winR&#xff0c;调出运行窗口&#xff0c;输入cmd就可以进入命令行了。在Win10以前的系统种&#xff0c;如果在命令行中再输入一个cmd&#xff0c;就会再打开一个命令行。但最近的Win11版本中&#xf…...

uniapp onLoad生命周期 uni.$on接受参数无法改变data数据解决办法

问题阐述&#xff1a; a: uni.$emit(name,data)uni.navigateTo({url:b})b:onload(){ uni.$on(name,(res)>{ this.nameres console.log(this.name) })}用以上写法来跨页面传参会发现在b页面&#xff0c;虽然能够接受到参数但是赋值到data时候没生效&#xff0c;虽然控制台能…...

Android Camera开发入门(4):USB/UVC Camera的使用

Android Camera开发入门(4):USB/UVC Camera的使用 本文基于开源项目https://github.com/saki4510t/UVCCamera之上进行二次封装和使用 在前几篇文章中,我们介绍了Camera到CameraX的基础功能应用,同时附上了相关代码,需要的源码的大佬们可以滑到最底部获取。 本篇我们一起…...

Java网络爬虫——jsoup快速上手,爬取京东数据。同时解决‘京东安全’防爬问题

文章目录 介绍jsoup使用1.解析url&#xff0c;获取前端代码2.解决京东安全界面跳转3.获取每一组的数据4.获取商品数据的具体信息4.最终代码 介绍 网络爬虫&#xff0c;就是在浏览器上&#xff0c;代替人类爬取数据&#xff0c;Java网络爬虫就是通过Java编写爬虫代码&#xff0…...

外观模式:简化复杂子系统的访问与使用

文章目录 1. 简介2. 外观模式的基本结构3. 外观模式的实现步骤4. 外观模式的应用与实例4.1 图形界面库的外观模式应用4.2 文件压缩与解压缩的外观模式应用4.3 订单处理系统的外观模式应用 5. 外观模式的优缺点5.1 优点5.2 缺点 6. 总结 1. 简介 外观模式是一种结构型设计模式&…...

代码随想录day38|509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯

509. 斐波那契数 class Solution:def fib(self, n: int) -> int:#dp含义&#xff0c;递推公式&#xff0c;dp初始化&#xff0c;遍历顺序&#xff0c;打印dpif n 0:return 0dp [0] * (n1)dp[0]0dp[1]1for i in range(2,n1):dp[i] dp[i-1] dp[i-2]return dp[n] 70. 爬楼梯…...

UE5 C++ UGameInstance 功能、作用及应用

# UE5 C UGameInstance 功能及作用 网上有很多文章介绍&#xff0c;例如在游戏中只有一个实例&#xff0c;换关卡不会丢失等。暂时省略。 # UE5 C UGameInstance 应用 ## 应用一&#xff0c;UE5 C UGameInstance 里监听player创建事件 UWebSocketGameInstance.h里的定义 …...

NodeJs-http模块

目录 一、概念二、请求报文的组成三、响应报文的组成四、创建http服务4.1 操作步骤4.2 注意事项 五、获取 HTTP 请求报文5.1 获取请求报文5.2 提取路径和查询字符串 六、设置 HTTP 响应报文七、MIME设置资源类型 一、概念 HTTP&#xff08;hypertext transport protocol&#…...

翻译句子 前面的路是非常狭窄的 不能翻译成 the ahead of road is narrow 的原因

翻译句子 前面的路是非常狭窄的。The road ahead is very narrow. 可以将句子翻译成 “The ahead of road is narrow.”&#xff0c;但这个翻译可能不太符合英语的表达习惯。更常见的表达方式是 “The road ahead is narrow.”&#xff0c;这样更符合英语的语法和习惯用法。 …...

NTT功能与实现

NTT的基础功用与拓展功能: 1.evaluate和interpolate evaluate的本质是选择n个点(假设f(x)的度为n)&#xff0c;计算得到其值&#xff0c;因此根据定义可以直接进行代入计算。为了加快计算的过程选取 w n w_n wn​的幂次(DFT问题即离散傅里叶变换)&#xff0c;使用FFT算法来加…...

Flutter(九)Flutter动画和自定义组件

目录 1.动画简介2.动画实现和监听3. 自定义路由切换动画4. Hero动画5.交织动画6.动画切换7.Flutter预置的动画过渡组件自定义组件1.简介2.组合组件3.CustomPaint 和 RenderObject 1.动画简介 Animation、Curve、Controller、Tween这四个角色&#xff0c;它们一起配合来完成一个…...

【python】可视化

柱状图 matplotlib之pyplot模块之柱状图&#xff08;bar()&#xff1a;基础参数、外观参数&#xff09;_plt.bar_mighty13的博客-CSDN博客 bar()的基础参数如下&#xff1a; x&#xff1a;柱子在x轴上的坐标。浮点数或类数组结构。注意x可以为字符串数组&#xff01; height&…...

C++继承多接口,调用虚函数跳转到错误接口的虚函数的奇怪问题

问题重现 定义了两个接口IA IB class IA{public:virtual void funA() = 0; }; class IB{public:virtual void funB() = 0; }...

C++:日期类

学习目标&#xff1a; 加深对四个默认构造函数的理解&#xff1a; 1.构造函数 2.析构函数 3.拷贝构造 4.运算符重载 实现功能 1.比较日期的大小 2.日期-天数 3.前/后置&#xff0c;-- 这里基本会使用运算符重载 定义一个日期类 class Date { public://1.全缺省参数的构造函数Da…...

c++ 学习之 构造函数的使用

上代码 class person { public:person(){cout << " person 的无参默认构造函数 " << endl;}person(int age){cout << " person 的有参默认构造函数 " << endl;m_age age;}person(const person& other){cout << "…...

算法通关村15关 | 超大规模数据场景常见问题

1.用4KB内存寻找重复元素 题目&#xff1a;给定一个数组&#xff0c;包含从1到N的整数&#xff0c;N最大为32000&#xff0c;数组可能还有重复值&#xff0c;且N的取值不定&#xff0c;若只有4KB的内存可用&#xff0c;该如何打印数组中所有重复元素。 分析&#xff1a; 本身是…...

qemu编译与使用

文章目录 1、安装依赖2、下载qemu源码3、编译4、运行5、qemu参数 qemu 是一个硬件虚拟化程序&#xff08;hypervisor that performs hardware virtualization&#xff09;&#xff0c;与传统的 VMware / VirtualBox 之类的虚拟机不同&#xff0c;它可以通过 binary translation…...