C++(11)类语法分析(2)

C++(10)之类语法分析(2)


Author: Once Day Date: 2024年8月17日

一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…

漫漫长路,有人对你微笑过嘛…

全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客

参考文章:

  • C++ 重载运算符和重载函数 | 菜鸟教程 (runoob.com)
  • 类和结构 (C++) | Microsoft Learn
  • C++ 类 & 对象_w3cschool
  • 复制构造函数和复制赋值运算符 (C++) | Microsoft Learn
  • 如何:定义移动构造函数和移动赋值运算符 (C++) | Microsoft Learn
  • 委托构造函数 (C++) | Microsoft Learn
  • 指向成员的指针 | Microsoft Learn
  • 对象生存期和资源管理 (RAII) | Microsoft Learn
  • 友元 (C++) | Microsoft Learn
  • 用户定义的类型转换 (C++) | Microsoft Learn

文章目录

  • C++(10)之类语法分析(2)
        • 1. 类高级特性
          • 1.1 运算符重载
          • 1.2 友元函数
          • 1.3 类型转换
          • 1.4 特殊成员函数
          • 1.5 禁用方法
          • 1.6 返回值类型
          • 1.7 内存处理(new/delete)
          • 1.8 嵌套结构体
          • 1.9 成员初始化列表

1. 类高级特性

基础类的介绍请参考文章:C++(10)类语法分析(1)-CSDN博客

1.1 运算符重载

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

当调用一个重载函数重载运算符时,编译器通过把所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策

运算符重载是C++的一个强大特性,它允许为用户定义的类型自定义运算符的行为。通过运算符重载,我们可以使自定义类型的对象支持各种运算符,如算术运算符、比较运算符、输入输出运算符等,从而提高代码的可读性和表现力。

运算符重载的语法,运算符重载的实现方式是定义一个特殊的成员函数或全局函数,函数名为operator后跟运算符符号。例如,重载+运算符的函数名为operator+

class MyClass {
public:
    MyClass operator+(const MyClass& other);  // 成员函数形式的运算符重载
};

MyClass operator+(const MyClass& lhs, const MyClass& rhs);  // 全局函数形式的运算符重载

重载算术运算符,可以重载各种算术运算符,如+-*/等,以支持自定义类型的算术运算。

class Complex {
public:
    Complex(double real, double imag) : real(real), imag(imag) {}

    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }

private:
    double real;
    double imag;
};

下面是可重载的运算符列表:

双目算术运算符+ (加),-(减),*(乘),/(除),% (取模)
关系运算符==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于)
逻辑运算符||(逻辑或),&&(逻辑与),!(逻辑非)
单目运算符+ (正),-(负),*(指针),&(取地址)
自增自减运算符++(自增),–(自减)
位运算符| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放new, delete, new[ ] , delete[]
其他运算符()(函数调用),->(成员访问),,(逗号),[](下标)

下面是不可重载的运算符列表:

  • .,成员访问运算符。

  • .*, ->\*,成员指针访问运算符。

  • ::,域运算符。

  • sizeof,长度运算符。

  • ?:,条件运算符。

  • #,预处理符号。

重载运算符还需要满足一下限制:

  • 重载运算符必须至少有一个操作数是用户定义的类型。这意味着不能重载两个内置类型的运算符。
  • 重载运算符的优先级和结合性是固定的,不能被改变。重载运算符的优先级和结合性与对应的内置运算符相同。
  • 重载运算符必须是公有的(public)成员函数或全局函数。如果是成员函数,则其中一个操作数必须是该类的对象。
  • 重载运算符时不能改变操作数的个数。例如,二元运算符重载后仍然是二元的,一元运算符重载后仍然是一元的。
  • 重载运算符时不能改变运算符的语法含义。例如,不能将加法运算符+重载为执行减法操作。
  • 重载运算符时不能创建新的运算符,只能重载已有的运算符。
  • 某些运算符必须以成员函数的形式进行重载,如赋值运算符=、下标运算符[]、函数调用运算符()和成员访问箭头运算符->。
  • 重载运算符时,参数列表中至少应有一个参数是类的对象或对象的引用。
  • 静态成员函数不能用于重载运算符。
1.2 友元函数

在C++中,友元函数(Friend Function)是一种特殊的函数,它可以访问类的私有成员和保护成员,即使它不是类的成员函数。这打破了类的封装性,但在某些情况下,友元函数可以提供更加灵活和方便的方式来操作类的私有数据。

要将一个函数声明为类的友元函数,需要在类的定义中使用关键字 friend,并在函数声明前加上 friend 关键字。友元函数可以是全局函数,也可以是另一个类的成员函数。

class MyClass {
private:
    int secret;

public:
    MyClass(int s) : secret(s) {}

    friend void friendFunction(MyClass& obj);
};

void friendFunction(MyClass& obj) {
    // 友元函数可以访问 MyClass 的私有成员
    std::cout << "My friend knows my secret: " << obj.secret << std::endl;
}

在上面的例子中,friendFunction 被声明为 MyClass 的友元函数。尽管 friendFunction 不是 MyClass 的成员函数,但它可以访问 MyClass 的私有成员 secret

当两个类需要频繁地访问对方的私有成员时,使用友元函数可以避免繁琐的公有接口设计。

1.3 类型转换

在C++中,类可以定义自动类型转换和强制类型转换,以实现类对象与其他类型之间的转换。

自动类型转换(Implicit Type Conversion):有一个名为 Fruit 的类,它表示一种水果。现在,把这个水果变成一个整数,代表这个水果的数量。可以通过定义一个接受 Fruit 对象的构造函数来实现这个功能。

class Fruit {
public:
    Fruit(int quantity) : quantity_(quantity) {}

    // 定义转换函数,将 Fruit 对象转换为整数
    operator int() const {
        return quantity_;
    }

private:
    int quantity_;
};

// 使用示例
Fruit apple(5);
int quantity = apple; // 自动将 Fruit 对象转换为整数

在这个例子中,定义了一个转换函数 operator int(),它允许 Fruit 对象自动转换为整数。将 Fruit 对象赋值给一个整数变量时,编译器会自动调用这个转换函数,将 Fruit 对象转换为对应的整数值。

隐式类型转换是自动进行的,不需要显式地调用转换函数。以下是一些常见的隐式类型转换场景:

  • 将类对象赋值给其他类型的变量。
  • 将类对象作为函数参数传递。
  • 在条件语句中使用类对象。
  • 返回值声明为类对象。

此外,这里定义了一个转换函数operator int() const,其格式通常如下:

operator typename();

转换函数必须是类方法,且不能指定返回类型,不能有参数,一般最好进行显示声明(explicit),可以避免隐式自动转换。

强制类型转换(Explicit Type Conversion):现在,让我们考虑一个更复杂的情况。假设有一个名为 Juice 的类,它表示一种果汁。想把整数转换为 Juice 对象,代表制作果汁所需的水果数量。但是这个转换不希望是自动进行的,而是要求显式地进行转换。

class Juice {
public:
    explicit Juice(int quantity) : quantity_(quantity) {}

private:
    int quantity_;
};

// 使用示例
Juice orangeJuice = Juice(10); // 正确,显式地创建 Juice 对象
Juice appleJuice = 5; // 错误,不允许自动转换

在这个例子中,使用 explicit 关键字来修饰 Juice 类的构造函数,表示不允许进行隐式转换。这意味着不能直接将整数赋值给 Juice 对象,而必须显式地调用构造函数来创建 Juice 对象。

通过使用 explicit 关键字,可以防止意外的自动转换,提高代码的可读性和安全性

自动类型转换允许类对象与其他类型之间的隐式转换,通过定义转换函数来实现。而强制类型转换要求显式地进行转换,通过使用 explicit 关键字来禁止自动转换。

当提供两个或多个用户定义的用于执行相同转换的转换时,该转换将被视为不明确。 这种不明确性是一个错误,因为编译器无法确定应选择哪一个可用转换

1.4 特殊成员函数

在C++类中,有一些特殊的成员函数,它们在类的生命周期中扮演着重要的角色。

  • 默认构造函数(Default Constructor),默认构造函数是一种特殊的构造函数,它不接受任何参数,或者接受的参数都有默认值。当创建一个类的对象时,如果没有显式地调用其他构造函数,默认构造函数会被自动调用。默认构造函数的作用是初始化对象的成员变量,将其设置为合适的默认值。

    class_name::class_name() {}
    
  • 默认析构函数(Default Destructor),默认析构函数是一种特殊的成员函数,它在对象的生命周期结束时被自动调用。析构函数的作用是释放对象所占用的资源,如动态分配的内存、打开的文件等。默认析构函数不接受任何参数,也没有返回值。

    class_name::~class_name() {}
    
  • 复制构造函数(Copy Constructor),复制构造函数是一种特殊的构造函数,它接受一个同类型对象的常量引用作为参数。复制构造函数的作用是根据已有对象创建一个新对象,并将已有对象的成员变量的值复制到新对象中。

    class_name(const class_name &);
    

    当以下情况发生时,复制构造函数会被调用:

    • 用一个对象初始化另一个同类型的对象。
    • 函数按值传递对象。
    • 函数返回一个对象。

    由于按值传递对象将调用复制构造函数,因此应该按照引用传递对象,可以节省调用构造函数的时间和存储新对象的空间。

    复制构造函数只是浅复制,即简单的复制非静态成员的值,所以指针指向的区域还是共用的,这种情况下需要定义一个复制构造函数,进行深度赋值操作。

  • 赋值运算符重载(Assignment Operator),赋值运算符重载是一种特殊的成员函数,它定义了对象之间的赋值操作。默认情况下,赋值运算符执行逐个成员的赋值操作。但是,有时需要自定义赋值运算符的行为,以处理特殊的赋值逻辑,如深拷贝、资源管理等。

    class_name & class_name::operator=(const class_name &);
    

    由于目标对象可能引用了以前分配的数据,所以函数应该使用delete[]来释放这些数据,同时应该避免将对象赋给自己。

  • 移动构造函数(Move Constructor),移动构造函数是C++11引入的新特性,它接受一个同类型对象的右值引用作为参数。移动构造函数的作用是将资源从一个对象转移到另一个对象,而不是进行复制。这可以提高性能,避免不必要的复制操作。

    class_name::class_name(class_name &&);
    

    移动构造函数需要对右值才能生效(没有实际存储空间的值),如下所示:

    class_name two = one;			// 匹配中复制构造函数
    class_name four (one + three); 	// 匹配中移动构造函数
    
  • 移动赋值运算符重载(Move Assignment Operator),移动赋值运算符重载也是C++11引入的新特性,它允许将一个对象的资源转移到另一个对象,而不是进行复制。与移动构造函数类似,移动赋值运算符重载接受一个同类型对象的右值引用作为参数,并将资源从源对象转移到目标对象。

    class_name & class_name::operator=(class_name &&);
    

    如果要对左值强行使用移动赋值语义,可以通过static_cast强制类型转换为class_name &&,或者使用utility中的std::move函数。

委托构造函数(Delegating Constructors),委托构造函数允许一个构造函数调用同一个类中的另一个构造函数。这样可以避免在多个构造函数中重复相同的初始化代码。

class Person {
public:
    Person(const std::string& name, int age) : name(name), age(age) {
        // ...
    }
    
    Person(const std::string& name) : Person(name, 0) {
        // ...
    }
    // ...
private:
    std::string name;
    int age;
};

在上述示例中,第二个构造函数委托给了第一个构造函数,并将年龄参数设置为默认值0。

继承构造函数(Inheriting Constructors):继承构造函数允许派生类直接使用基类的构造函数,而无需在派生类中显式定义对应的构造函数。这可以简化派生类的构造函数编写。

class Base {
public:
    Base(int x) : x(x) {
        // ...
    }
    // ...
protected:
    int x;
};

class Derived : public Base {
public:
    using Base::Base;
    // ...
};

在上述示例中,派生类 Derived 使用 using Base::Base; 语句继承了基类 Base 的构造函数。这样,Derived 类可以直接使用 Base 类的构造函数,而无需显式定义自己的构造函数。

1.5 禁用方法

默认方法(Default Functions),默认方法是指编译器自动生成的特殊成员函数,包括默认构造函数、析构函数、复制构造函数和赋值运算符重载函数。在C++11之前,如果没有显式定义这些函数,编译器会自动生成它们的默认实现。但是,从C++11开始,可以使用 =default 语法来显式地指定使用编译器生成的默认实现。

class MyClass {
public:
    MyClass() = default;
    ~MyClass() = default;
    MyClass(const MyClass&) = default;
    MyClass& operator=(const MyClass&) = default;
    // ...
};

使用 =default 可以明确表示使用编译器生成的默认实现,提高代码的可读性和维护性。

禁用方法(Deleted Functions),禁用方法是指使用 =delete 语法来明确禁止使用某个函数。当我们不希望某个函数被调用时,可以将其声明为禁用方法。这样,如果在代码中尝试调用被禁用的函数,编译器会报错。

class NonCopyable {
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    // ...
};

在上述示例中,通过将复制构造函数和赋值运算符重载函数声明为禁用方法,我们可以禁止对象的复制和赋值操作

1.6 返回值类型

在C++类中,函数的返回值类型可以是值、常量对象、引用或常量引用。不同的返回值类型在不同的场景下有其特定的用途。

(1) 值返回(Return by Value):当函数返回一个值时,它会创建一个临时对象,并将函数内部对象的值复制到该临时对象中。这个临时对象将作为函数的返回值被返回给调用方。值返回适用于以下情况:

  • 返回的对象较小,复制成本较低。
  • 返回的对象是函数内部创建的,没有与外部共享状态。
  • 不需要在函数外部修改返回的对象。
class Point {
public:
    int getX() const { return x; }
    // ...
private:
    int x;
    // ...
};

(2) 常量对象返回(Return by Const Object),当函数返回一个常量对象时,它表示返回的对象不能被修改。这提供了一种安全性,确保调用方不会意外地修改返回的对象。常量对象返回适用于以下情况:

  • 返回的对象不应被修改。
  • 希望强调返回对象的不可变性。
class Date {
public:
    const std::string& getFormattedDate() const {
        // ...
        return formattedDate;
    }
    // ...
private:
    std::string formattedDate;
    // ...
};

(3) 引用返回(Return by Reference):当函数返回一个引用时,它返回的是对象的引用,而不是对象的副本。引用返回允许在函数外部直接访问和修改函数内部的对象。引用返回适用于以下情况:

  • 返回的对象是函数外部已经存在的对象。
  • 希望在函数外部能够修改返回的对象。
  • 返回的对象较大,复制成本较高,希望避免不必要的复制。
class Array {
public:
    int& operator[](size_t index) {
        return data[index];
    }
    // ...
private:
    int data[100];
    // ...
};

(4) 常量引用返回(Return by Const Reference):当函数返回一个常量引用时,它返回的是对象的引用,但该引用不允许修改对象。常量引用返回提供了访问对象的能力,同时确保了对象的不可变性。常量引用返回适用于以下情况:

  • 返回的对象是函数外部已经存在的对象。
  • 希望在函数外部能够访问返回的对象,但不允许修改它。
  • 返回的对象较大,复制成本较高,希望避免不必要的复制。
class Person {
public:
    const std::string& getName() const {
        return name;
    }
    // ...
private:
    std::string name;
    // ...
};
1.7 内存处理(new/delete)

在C++中,使用new运算符动态分配对象时,会先分配内存,然后调用类的构造函数来初始化对象。相应地,使用delete运算符释放对象时,会先调用类的析构函数来清理资源,然后再释放内存。

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor called" << std::endl;
    }
    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    MyClass* obj = new MyClass();  // 分配内存并调用构造函数
    delete obj;                    // 调用析构函数并释放内存
    return 0;
}

定位new运算符(Placement New Operator),定位new运算符允许在已分配的内存上构造对象。它接受一个指向内存位置的指针作为参数,并在该位置构造对象。定位new运算符通常用于在特定的内存位置上构造对象,如在预分配的内存池中创建对象。

int main() {
    char* buffer = new char[sizeof(MyClass)];  // 分配原始内存
    MyClass* obj = new (buffer) MyClass(42);   // 使用定位new在buffer上构造对象
    obj->~MyClass();                           // 显式调用析构函数
    delete[] buffer;                           // 释放原始内存
    return 0;
}

在某些情况下,我们可能需要显式调用对象的析构函数,而不是依赖于delete运算符。这通常发生在使用定位new运算符构造对象时,或者在使用placement new构造对象数组时

int main() {
    char* buffer = new char[sizeof(MyClass) * 3];  // 分配原始内存
    MyClass* obj1 = new (buffer) MyClass();        // 使用定位new构造对象
    MyClass* obj2 = new (buffer + sizeof(MyClass)) MyClass();
    MyClass* obj3 = new (buffer + sizeof(MyClass) * 2) MyClass();
    
    obj3->~MyClass();  // 显式调用析构函数
    obj2->~MyClass();
    obj1->~MyClass();
    
    delete[] buffer;   // 释放原始内存
    return 0;
}

在上述示例中,我们使用定位new在预分配的内存上构造了多个对象。由于没有使用delete运算符,我们需要显式调用每个对象的析构函数来进行清理。注意,析构函数的调用顺序与构造顺序相反。

1.8 嵌套结构体

在C++中,可以在一个类或结构体内部定义另一个类或结构体,这种内部定义的类或结构体称为嵌套类(Nested Class)或嵌套结构体(Nested Struct)。嵌套类和嵌套结构体提供了一种将相关类或结构体组织在一起的方式,增强了代码的可读性和封装性。

嵌套类(Nested Class),嵌套类是在另一个类的内部定义的类。它可以访问外部类的成员,包括私有成员和保护成员。嵌套类通常用于实现与外部类密切相关的功能,或者用于隐藏实现细节。

class Outer {
public:
    void outerMethod() {
        Inner inner;
        inner.innerMethod();
    }
private:
    class Inner {
    public:
        void innerMethod() {
            std::cout << "Inner method called" << std::endl;
        }
    };
};

在上述示例中,Inner类是在Outer类内部定义的嵌套类。Outer类的成员函数outerMethod()可以直接创建和使用Inner类的对象。

嵌套结构体(Nested Struct),嵌套结构体与嵌套类类似,只是使用struct关键字定义。嵌套结构体的成员默认为公有访问权限,而嵌套类的成员默认为私有访问权限。

struct Outer {
    void outerMethod() {
        Inner inner;
        inner.x = 10;
    }
    struct Inner {
        int x;
    };
};

在上述示例中,Inner结构体是在Outer结构体内部定义的嵌套结构体。Outer结构体的成员函数outerMethod()可以直接访问和修改Inner结构体的成员变量x

要在外部类或结构体之外访问嵌套类或嵌套结构体,需要使用外部类或结构体的作用域解析运算符::

class Outer {
public:
    class Inner {
    public:
        void innerMethod() {
            std::cout << "Inner method called" << std::endl;
        }
    };
};

int main() {
    Outer::Inner inner;
    inner.innerMethod();
    return 0;
}

在上述示例中,我们在main()函数中创建了Outer::Inner类型的对象,并调用了其成员函数innerMethod()

1.9 成员初始化列表

在C++中,成员初始化列表(Member Initialization List)是一种在构造函数中初始化类成员变量的方式。它提供了一种更高效、更清晰的方法来初始化成员变量,特别是对于常量成员、引用成员以及没有默认构造函数的类类型成员。

常见使用场景如下:

  • 常量成员变量:常量成员变量必须在构造函数的成员初始化列表中初始化,因为它们不能在构造函数体内被赋值。
  • 引用成员变量:引用成员变量必须在构造函数的成员初始化列表中初始化,因为引用必须在定义时绑定到一个对象。
  • 没有默认构造函数的类类型成员:如果类成员变量是没有默认构造函数的类类型,那么必须在成员初始化列表中显式初始化它们。
  • 基类构造函数初始化:在派生类的构造函数中,必须使用成员初始化列表来调用基类的构造函数。
  • 提高效率:对于内置类型和有默认构造函数的类类型成员,使用成员初始化列表可以避免默认构造和再赋值的过程,提高效率。

使用限制如下:

  • 成员初始化列表中的成员变量初始化顺序与它们在类中声明的顺序相同,而不是按照成员初始化列表中的顺序。
  • 成员初始化列表不能用于初始化静态成员变量,静态成员变量需要在类外部单独初始化。
  • 如果一个成员变量在成员初始化列表中被初始化,就不能再在构造函数体内对其赋值,否则会报错。
class MyClass {
public:
    MyClass(int a, const std::string& s) : constantMember(42), referenceMember(s), intMember(a), 	
    	objectMember(a, s) {
        // 构造函数体
    }
private:
    const int constantMember;
    const std::string& referenceMember;
    int intMember;
    SomeClass objectMember;
};

在上述示例中,MyClass的构造函数使用成员初始化列表来初始化常量成员constantMember、引用成员referenceMember、内置类型成员intMember以及类类型成员objectMember。成员初始化列表中的初始化顺序与成员变量在类中声明的顺序相同。

C++11引入了类内初始化(In-class Initialization)特性,允许在类内部直接为非静态成员变量提供默认初始值。这样可以简化构造函数的编写,同时提供了一种更清晰、更一致的方式来初始化成员变量。

class MyClass {
public:
    MyClass(int a) : intMember(a) {
        // 构造函数体
    }
private:
    int intMember = 0;
    std::string stringMember = "Default";
    std::vector<int> vectorMember{1, 2, 3};
};

在上述示例中,intMemberstringMembervectorMember都在类内部提供了默认初始值。如果构造函数没有显式初始化这些成员变量,它们将使用类内部指定的默认值。

需要注意的是,类内初始化不能用于常量成员、引用成员以及没有默认构造函数的类类型成员,这些成员变量仍然需要在成员初始化列表中显式初始化







Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~

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

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

相关文章

Python数据结构:集合详解(创建、集合操作)④

文章目录 1. Python集合概述2. 创建集合2.1 使用花括号 {} 创建集合2.2 使用 set() 函数创建集合2.3 创建空集合 3. 集合操作3.1 添加和删除元素3.2 集合的基本操作3.3 集合的比较操作3.4 不可变集合&#xff08;frozenset&#xff09; 4. 综合例子&#xff1a;图书管理系统 Py…

30秒内批量删除git本地分支

在开发过程中&#xff0c;我们经常需要对本地的 Git 分支进行管理。有时&#xff0c;由于各种原因&#xff0c;我们可能需要批量删除本地的分支。这可能是因为某些分支已经不再需要&#xff0c;或者是为了清理本地的分支列表&#xff0c;以保持整洁和易于管理。 要批量删除本地…

没有用的小技巧之---接入网线,有内网没有外网,但是可以登录微信

打开控制面板&#xff0c;找到网络和Internet 选择Internet选项 点击连接&#xff0c;选择局域网设置 取消勾选代理服务器

开放式耳机会打扰到别人吗?四款漏音处理做的好的蓝牙耳机

一般情况下&#xff0c;开放式耳机不会打扰到别人。 开放式耳机通常采用全开放设计&#xff0c;声音不会完全封闭在耳朵里&#xff0c;而是向四周扩散&#xff0c;相比封闭式耳机&#xff0c;其对外界环境的噪音影响更小 。而且现在的开放式耳机在技术上已经有了很大的进步&am…

[数据集][目标检测]工程机械车辆检测数据集VOC+YOLO格式3189张10类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;3189 标注数量(xml文件个数)&#xff1a;3189 标注数量(txt文件个数)&#xff1a;3189 标注…

springboot的自动配置和怎么做自动配置

目录 一、Condition 1、Condition的具体实现 2、Condition小结 &#xff08;1&#xff09;自定义条件 &#xff08;2&#xff09;SpringBoot 提供的常用条件注解 二、Enable注解 三、EnableAutoConfiguration 注解和自动配置 1、EnableAutoConfiguration的三个注解属性…

git 学习--GitHub Gitee码云 GitLab

1 集中式和分布式的区别 1.1 集中式 集中式VCS必须有一台电脑作为服务器&#xff0c;每台电脑都把代码提交到服务器上&#xff0c;再从服务器下载代码。如果网络出现问题或服务器宕机&#xff0c;系统就不能使用了。 1.2 分布式 分布式VCS没有中央服务器&#xff0c;每台电脑…

Python编码系列—Python SQL与NoSQL数据库交互:深入探索与实战应用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

预警先行,弯道哨兵让行车更安全

预警先行&#xff0c;弯道哨兵让行车更安全”这句话深刻体现了现代交通安全理念中预防为主、科技赋能的重要性。在道路交通中&#xff0c;尤其是复杂多变的弯道区域&#xff0c;交通事故的发生率往往较高&#xff0c;因此&#xff0c;采取有效的预警措施和引入先进的交通辅助设…

怎么管控终端电脑上的移动端口

管控终端电脑上的移动端口&#xff0c;尤其是USB等移动端口&#xff0c;是确保企业数据安全和提升网络管理效率的重要手段。 一、使用注册表编辑器禁用USB端口&#xff08;适用于Windows系统&#xff09; 打开注册表编辑器&#xff1a; 同时按下“WinR”组合键&#xff0c;打…

SEO优化:如何优化自己的文章,解决搜索引擎不收录的问题

可以使用bing的URL检查&#xff0c;来检查自己的文章是不是负荷收录准测&#xff0c;如果页面有严重的错误&#xff0c;搜索引擎是不会进行收录的&#xff0c;而且还会判定文章为低质量文章&#xff01; 检查是否有问题。下面的页面就是有问题&#xff0c;当然如果是误报你也可…

Java并发类API——CompletionService

CompletionService 是 Java 中 java.util.concurrent 包的一部分&#xff0c;用于管理并发任务的执行&#xff0c;并以完成的顺序提供结果。它结合了线程池和阻塞队列的功能&#xff0c;用于提交任务并按照任务完成的顺序来检索结果&#xff0c;而不是按照任务提交的顺序。 接…

NC拼接所有的字符串产生字典序最小的字符串

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个长度…

单例模式(singleton)- python实现

通俗示例 想象一下&#xff0c;一个国家只有一个国王。不管你在哪里&#xff0c;提到这个国家的国王&#xff0c;大家都能知道是指同一个人。在程序设计中&#xff0c;单例模式就像是这样的国王&#xff0c;一个类只有一个实例&#xff0c;无论你多少次请求这个类的实例&#…

基于Hadoop的汽车大数据分析系统设计与实现【爬虫、数据预处理、MapReduce、echarts、Flask】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍爬虫数据概览HIve表设计Cars Database Tables1. cars_data2. annual_sales_volume3. brand_sales_volume4. city_sales_volume5. sales_volume_by_year_and_brand6. sales_distribu…

Midjourney进阶-反推与优化提示词(案例实操)

​ Midjourney中提示词是关键&#xff0c;掌握提示词的技巧直接决定了生成作品的质量。 当你看到一张不错的图片&#xff0c;想要让Midjourney生成类似的图片&#xff0c;却不知道如何描述画面撰写提示词&#xff0c;这时候Midjourney的/describe指令&#xff0c;正是帮助你推…

嵌入式AI快速入门课程-K510篇 (第四篇 AI概念及理论知识)

第四篇 AI概念及理论知识 文章目录 第四篇 AI概念及理论知识1.人工智能与机器学习1.1 机器学习1.2 模型和拟合1.3 线性回归模型1.3.1 实现简单线性回归1.3.2 简单线性回归代码解析1.3.3 Sklearn实现房价预测模型1.3.4 Sklearn房价预测代码解析 2.深度学习及神经网络2.1 深度学习…

Java | Leetcode Java题解之第355题设计推特

题目&#xff1a; 题解&#xff1a; class Twitter {private class Node {// 哈希表存储关注人的 IdSet<Integer> followee;// 用链表存储 tweetIdLinkedList<Integer> tweet;Node() {followee new HashSet<Integer>();tweet new LinkedList<Integer&g…

多线程并发服务器

多线程并发服务器 服务端 #include <stdio.h> #include <string.h> #include <sys/errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <ctype.h> #include <p…

Nofollow不好吗?Follow和Nofollow的区别

Follow和Nofollow的区别 “follow”和“nofollow”是HTML中的两种属性&#xff0c;它们通常用于<a>标签&#xff0c;即超链接。这两种属性对搜索引擎优化&#xff08;SEO&#xff09;有重要的影响。 1.Follow链接&#xff08;Dofollow&#xff09;: 这是默认的链接属性…