C-面向对象的特性

面向对象

面向对象(Object-Oriented, 简称OO)是一种编程范式或编程风格,它使用“对象”来设计软件。面向对象的思想将现实世界中的事物(称为“对象”)映射到软件系统中,使得软件的设计更加符合人类的思维方式,提高了软件的可重用性、可维护性和可扩展性。

面向对象编程(Object-Oriented Programming, 简称OOP)是这种思想的具体实现方式。

三大特性

面向对象三大特性:封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。

封装
  • 作用:封装是面向对象编程的核心思想之一。它通过将对象的属性和方法结合在一个单独的单元中,并对对象内部的细节进行隐藏,只对外提供有限的接口(方法)来与对象进行交互。这样做的好处是提高了数据的安全性,防止外部代码直接访问或修改对象的内部状态,同时也降低了系统的耦合度,使得各个部分更加独立和易于维护。
继承
  • 作用:继承是面向对象编程中实现代码复用的一种机制。它允许我们定义一个类(子类)来继承另一个类(父类)的属性和方法。子类可以继承父类的所有非私有属性和方法,并且可以添加新的属性和方法或覆盖(重写)父类的方法。继承提高了代码的重用性,使得我们不需要从头开始编写代码,而是可以在现有代码的基础上进行扩展和定制。
多态
  • 作用:多态性是面向对象编程中一个非常重要的概念,它允许我们以统一的接口去调用不同的实现,从而提高代码的灵活性和可扩展性。多态性主要分为编译时多态(方法重载)和运行时多态(动态绑定或方法重写)。通过多态,我们可以在不知道对象具体类型的情况下,调用对象的方法,而方法的实际执行将取决于对象的类型。
重载、重写和隐藏
  • 重载:是同一类中方法的多态性表现,通过不同的参数列表来区分不同的方法。
  • 重写:是子类对父类方法的重新实现,发生在具有继承关系的类之间,且必须保持方法签名的一致性。即方法名称、参数列表和返回类型必须与被重写的方法完全相同
  • 隐藏:是派生类函数对基类同名函数的屏蔽,无论参数列表是否相同,都会导致基类函数被隐藏。
多态

多态允许我们将父类类型的指针或引用指向子类对象,并通过该指针或引用来调用成员函数,而具体调用的是哪个类的成员函数,则在运行时决定,这增加了程序的灵活性和可扩展性。

示例函数:一个C++中多态性的示例,通过虚函数来实现。在这个例子中,我们创建一个基类Animal,它有一个虚函数makeSound()。然后,我们将创建两个派生类DogCat,它们分别重写(Override)了makeSound()函数来提供各自的实现。

#include <iostream>
// 基类
class Animal {
public:
    // 虚函数
    virtual void makeSound() {
        std::cout << "Some generic animal sound\n";
    }
    // 虚析构函数,确保通过基类指针删除派生类对象时能够正确调用派生类的析构函数
    virtual ~Animal() {}
};

// 派生类 Dog
class Dog : public Animal {
public:
    // 重写虚函数
    void makeSound() override { // C++11及以后版本推荐使用override关键字
        std::cout << "Woof!\n";
    }
};

// 派生类 Cat
class Cat : public Animal {
public:
    // 重写虚函数
    void makeSound() override {
        std::cout << "Meow!\n";
    }
};

// 使用多态性的函数
void makeItSound(Animal* animal) {
    animal->makeSound(); // 运行时根据animal指向的实际对象类型调用相应的makeSound()
}

int main() {
    Animal* myAnimal = new Dog(); // 基类指针指向派生类对象
    makeItSound(myAnimal); // 输出: Woof!
    delete myAnimal; // 释放内存

    myAnimal = new Cat(); // 现在基类指针指向另一个派生类对象
    makeItSound(myAnimal); // 输出: Meow!
    delete myAnimal; // 再次释放内存

    return 0;
}

在该例中,makeItSound函数接受一个指向Animal类型的指针。由于makeSound是一个虚函数,因此当makeItSound被调用时,实际调用的是指针所指向对象的makeSound函数版本,这取决于指针在运行时指向的具体对象类型(即DogCat)。

虚函数、纯虚函数

在C++中,虚函数和纯虚函数允许派生类重写基类的成员函数,以实现不同的行为。

虚函数
  • 定义:在基类中,使用virtual关键字声明的成员函数称为虚函数。虚函数允许在派生类中被重写(Override),以提供特定于派生类的实现。
  • 用途:虚函数用于实现多态性,允许基类指针或引用指向派生类对象,并通过该指针或引用来调用成员函数,而具体调用哪个函数则在运行时决定。
纯虚函数
  • 定义:在基类中,使用virtual关键字和= 0语法声明的成员函数称为纯虚函数。纯虚函数没有实现(即没有函数体),它要求派生类必须提供该函数的实现。
  • 用途:纯虚函数用于定义接口,强制派生类实现特定的成员函数。它允许基类定义一组操作,但不提供这些操作的具体实现,而是由派生类来提供。

示例

// 基类
class Base {
public:
    // 虚函数
    virtual void show() {
        std::cout << "Base show" << std::endl;
    }

    // 纯虚函数
    virtual void pureVirtualFunction() = 0;

    // 虚析构函数
    virtual ~Base() {}
};

// 派生类
class Derived : public Base {
public:
    // 重写虚函数
    void show() override {
        std::cout << "Derived show" << std::endl;
    }

    // 必须实现纯虚函数
    void pureVirtualFunction() override {
        std::cout << "Derived pureVirtualFunction" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived(); // 基类指针指向派生类对象
    basePtr->show(); // 调用Derived的show()
    basePtr->pureVirtualFunction(); // 调用Derived的pureVirtualFunction()

    delete basePtr; // 释放内存
    return 0;
}
虚函数的实现机制

虚函数在C++中的实现机制主要涉及虚函数表和虚指针(vptr)的使用,以及编译器如何通过这些机制在运行时确定应该调用哪个函数。

虚函数表
  • 创建:编译器为每个包含虚函数的类创建一个虚函数表。虚函数表中的元素是指向类中所有虚函数地址的指针,按照虚函数在类中声明的顺序排列。
  • 内容:虚函数表中存储的是虚函数的地址,这些地址指向类中定义的虚函数实现。
  • 继承:当子类继承基类时,如果子类重写了基类的虚函数,则子类的虚函数表中相应位置的指针将指向子类重写的函数地址;如果子类没有重写基类的某个虚函数,则该位置的指针仍指向基类中的函数地址。
虚指针
  • 定义:编译器为每个包含虚函数的类对象添加一个隐藏的虚指针(vptr),该指针指向对象所属类的虚函数表。
  • 位置:虚指针通常作为类对象的第一个成员变量(但C++标准没有明确要求这一点,实际位置由编译器决定)。
  • 作用:在调用虚函数时,通过对象的虚指针找到其所属类的虚函数表,再根据虚函数表找到并调用相应的虚函数实现。
动态联编(Dynamic Binding)
  • 过程:当通过基类指针或引用来调用虚函数时,编译器会在运行时通过虚指针和虚函数表来确定应该调用哪个类的虚函数实现。这种机制称为动态联编或晚期绑定。
  • 对比:与静态联编(在编译时确定函数调用)不同,动态联编允许在运行时根据对象的实际类型来确定函数调用,从而实现了多态性。
虚函数表的访问
  • 方式:当执行虚函数调用时,编译器首先检查调用指针或引用的类型,如果指向的类中有虚函数表,则通过虚指针找到虚函数表,然后在表中根据函数声明的位置索引找到相应的函数指针,并调用该函数。
  • 示例:假设有基类Base和派生类DerivedBase中定义了一个虚函数show()。当通过基类指针Base* ptr = new Derived(); ptr->show();调用show()时,编译器会首先找到ptr指向对象的虚函数表,然后在表中找到show()函数的地址,并调用Derived类中重写的show()函数(如果Derived类重写了show())。
构造、析构函数的虚化

构造函数不能定义为虚函数,而析构函数可以定义为虚函数。

  • 初始化的需要:构造函数的主要目的是初始化对象。在面向对象编程中,对象的初始化通常与其确切类型紧密相关,因此不需要多态性来决定使用哪个构造函数。
  • 资源释放需求:在析构函数中,通常会释放对象占用的资源(如动态分配的内存、文件句柄等)。如果析构函数不是虚函数,那么派生类特有的资源可能无法被正确释放。
多继承的问题

多继承在C++中是一种常见的代码复用机制,在JAVA等语言中不支持。容易出现命名冲突、二义性和菱形继承等问题

  • 命名冲突:当多个父类中存在相同名称的属性和方法时,子类在调用这些成员时会产生二义性,编译器无法确定使用哪个父类的成员。

  • 二义性:类似于命名冲突,但更侧重于成员函数。如果多个父类中都实现了同一个成员函数,并且子类没有覆盖该成员函数,那么在使用该成员函数时会产生二义性,编译器无法确定调用哪个父类的成员函数。

  • 菱形继承(钻石继承):一个类同时继承了两个不相关的父类,并且这两个父类又继承了同一个父类,从而形成一个菱形的继承关系。这会导致基类成员在派生类中有多个拷贝,造成内存浪费和数据冗余,并且可能引发二义性问题。

深拷贝和浅拷贝

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在进行对象或数据结构复制时常用的两种方式,它们之间的主要区别在于复制的程度以及对原始对象内部结构的影响。

浅拷贝:
  • 浅拷贝是指创建一个新的对象,但只复制原始对象的基本数据类型的字段或引用(地址),而不复制引用指向的对象。
  • 这意味着新对象和原始对象中的引用指向相同的对象。如果原始对象中的字段是基本数据类型,那么这些字段会被复制到新对象中;而如果字段是引用类型,则新对象和原始对象的对应字段将引用同一个对象。
  • 因此,对新对象所做的修改可能会影响到原始对象,因为它们共享相同的引用。
深拷贝:
  • 深拷贝是指创建一个新的对象,并且递归地复制原始对象的所有字段和引用指向的对象,而不仅仅是复制引用本身。
  • 深拷贝会递归复制整个对象结构,包括对象内部的对象,确保新对象和原始对象之间的所有关系都是独立的。
  • 这意味着对新对象所做的修改不会影响到原始对象,因为它们拥有彼此独立的副本。
具体区别:
  • 浅拷贝:简单地把指向别人的值的一个指针给复制过来,新对象和原始对象共享某些引用类型的数据。
  • 深拷贝:实实在在地把别人的值给复制过来,新对象和原始对象在内存中是完全独立的。
实现方式
  • 浅拷贝:可以使用一些内置方法如列表的 .copy() 方法、list() 函数,或使用 copy 模块的 copy() 函数。
  • 深拷贝:通常使用 copy 模块的 deepcopy() 函数。
默认构造函数

在C++中,默认构造函数(Default Constructor)是一种特殊的构造函数,它在没有显式提供任何参数的情况下被调用以创建类的对象。默认构造函数可以是用户定义的,也可以是编译器自动生成的。

  • **编译器默认构造函数:**如果类中没有定义任何构造函数(无论是默认构造函数还是带参数的构造函数),编译器会隐式地生成一个默认构造函数。这个构造函数是公有的,并且不接受任何参数。
  • **用户定义默认构造函数:**用户可以通过不接受任何参数或所有参数都有默认值的构造函数来显式地定义一个默认构造函数。但是,C++11及更高版本提供了一种更直接的方式来声明默认构造函数,即使它带有参数列表(尽管这些参数都有默认值)。这就是使用= default;语法。
class MyClass {  
public:  
    MyClass() = default; // 显式地声明为默认构造函数(即使它没有参数)  
    // 或者,如果构造函数带有参数但所有参数都有默认值,也可以被视为默认构造函数  
    // MyClass(int a = 0, double b = 0.0) {}  
};
禁止构造函数

在面向对象编程中,有时我们可能希望禁止对象的实例化,即不希望其他代码通过调用类的构造函数来创建类的实例。这通常是因为该类被设计为工具类、静态类或者仅包含静态成员和静态方法,不依赖于类的实例状态。

在C++中,可以通过将构造函数声明为private来禁止外部代码创建类的实例。也可以将析构函数声明为privateprotected(通常推荐protected,因为它允许类的析构函数在派生类中被调用,这是RAII模式所必需的)。

class Utility {  
private:  
    // 禁止外部代码通过构造函数创建实例  
    Utility() {}  
    // 如果还需要防止继承,可以将析构函数也设为private或protected  
    // 但通常,设为protected更合适  
    // ~Utility() {}  
public:  
    // 静态成员函数  
    static void doSomething() {  
        // ...  
    }  
};

C++11引入了delete关键字,它提供了一种更直接、更明确的方式来禁止或删除成员函数。通过在函数声明后加上= delete,可以告诉编译器这个函数被删除了,不能被调用。

class Uncopyable {  
public:  
    Uncopyable() = default; // 使用默认构造函数  
    Uncopyable(const Uncopyable&) = delete; // 禁止拷贝构造函数  
    Uncopyable& operator=(const Uncopyable&) = delete; // 禁止赋值运算符  
    ~Uncopyable() = default; // 使用默认析构函数  
};
减少构造函数开销

在C++中,减少构造函数(Constructor)的开销是一个重要的性能优化方向,特别是当构造函数被频繁调用,或者处理大量对象时。构造函数时尽量使用类初始化列表,会减少调用默认的构造函数产生的开销。

  • 使用初始化列表可以提高性能,因为它允许直接初始化成员变量,避免了在构造函数体中先默认初始化成员变量然后再赋值的开销。
  • 初始化列表紧跟在构造函数的参数列表之后,冒号(:)开头,后跟成员变量名和它们的初始化值,成员之间用逗号分隔。
#include <iostream>  
#include <string>  
class MyClass {  
private:  
    int a;  
    double b;  
    std::string c;  
public:  
    // 使用初始化列表的构造函数  
    MyClass(int x, double y, const std::string& z)  
        : a(x), b(y), c(z) // 初始化列表  
    {  
        // 构造函数体可以为空,因为成员已经在初始化列表中初始化了  
    }  
    void print() const {  
        std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;  
    }  
}; 
委托构造函数

初始化列表还可以用于委托构造函数(C++11及更高版本),即一个构造函数调用同一个类中的另一个构造函数来初始化对象。但是,这并不是通过初始化列表直接完成的,而是通过在构造函数体内部调用另一个构造函数(注意,这种调用必须是构造函数体中的第一条语句,且只能是构造函数体中的第一条语句)。

class MyClass {  
public:  
    MyClass() : MyClass(0, 0.0, "Default") {} // 委托构造函数  
    MyClass(int x, double y, const std::string& z) : a(x), b(y), c(z) {}  
    // ... 其他成员和函数 ...  
};
友元函数

友元函数在C++中扮演着特殊的角色,它允许一个非成员函数访问类的私有成员和保护成员。(不建议使用,过度使用友元函数可能会降低代码的安全性和可维护性)

特点
  • 访问私有成员:友元函数可以访问类的私有成员和保护成员,这是它最基本也是最重要的作用。通过友元函数,类可以允许外部函数或类在必要时访问其内部数据,而无需将这些数据公开为公有成员。
  • 提高程序灵活性:友元函数的存在使得类的设计更加灵活。在需要时,可以通过声明友元函数来扩展类的功能,而无需修改类的内部实现。
  • 提高执行效率:友元函数可以直接访问类的私有成员,而不需要通过公有接口(如getter和setter函数)进行访问。这可以减少函数调用的开销,从而提高程序的执行效率。
用处
  • 运算符重载:在某些情况下,友元函数被用于运算符重载。例如,当需要为自定义类型实现自定义的算术运算时,可以将这些运算定义为友元函数,以便它们能够访问类的私有成员。
  • 友元类:当两个或多个类需要共享数据时,可以将一个类声明为另一个类的友元类,或者将某个函数声明为这些类的友元函数。这样,这些类或函数就可以访问彼此的私有成员,从而实现数据共享。
  • 全局函数访问私有成员:当需要在全局范围内定义一个函数来访问类的私有成员时,可以将该函数声明为友元函数。这样,该函数就可以在不破坏类封装性的前提下访问类的私有成员。
#include <iostream>  
using namespace std;  
  
// 声明友元函数  
double calculateArea(const Circle& c);  
class Circle {  
private:  
    double radius; // 私有成员变量  
public:  
    // 构造函数  
    Circle(double r = 0.0) : radius(r) {}  
    // 设置半径的公有成员函数  
    void setRadius(double r) {  
        radius = r;  
    }  
    // 声明友元函数  
    friend double calculateArea(const Circle& c);  
};  

// 定义友元函数  
double calculateArea(const Circle& c) {  
    // 直接访问私有成员变量radius  
    return 3.14159 * c.radius * c.radius;  
}  

int main() {  
    return 0;  
}
编译时多态和运行时多态

在面向对象编程中,多态性是一个核心概念,它允许我们以统一的方式处理不同类型的对象。多态性可以分为两种主要类型:编译时多态(也称为静态多态或早绑定)和运行时多态(也称为动态多态或晚绑定)。

编译时多态(静态多态)

编译时多态主要通过函数重载(Function Overloading)和模板(Templates)实现。

  • 函数重载:在同一作用域内,可以声明几个功能类似的同名函数,但这些函数的参数列表(参数个数、类型或顺序)必须不同。编译器在编译时会根据函数的参数列表和调用时提供的参数类型来选择最合适的函数进行调用。这种多态性在编译时就已经确定了,因此称为编译时多态。

  • 模板:模板是C++支持泛型编程的工具,它允许程序员编写与类型无关的代码。通过模板,可以创建函数模板和类模板。当编译器看到模板的使用时,它会根据提供的具体类型来生成相应的函数或类代码。这也是在编译时确定的,因此也被归类为编译时多态。

运行时多态(动态多态)

运行时多态主要通过虚函数(Virtual Functions)和继承(Inheritance)实现,它允许在基类的指针或引用上调用派生类中的成员函数。

  • 虚函数:在基类中,可以将某个成员函数声明为虚函数,这意味着该函数在派生类中可以被重写(Override)。当通过基类类型的指针或引用来调用虚函数时,实际调用的是指针或引用所指向的对象的成员函数版本。这个决定是在运行时做出的,因为编译器在编译时无法确定指针或引用将指向哪个派生类的对象。这种多态性称为运行时多态。

  • 抽象基类:通常,至少含有一个纯虚函数的类被称为抽象基类。抽象基类不能被实例化,但可以用作派生类的基类。纯虚函数是必须在派生类中重写的虚函数,它在基类中没有实现。抽象基类在面向对象的设计中非常重要,因为它们允许定义一组接口,这些接口在派生类中具体实现。

简述区别
  • 编译时多态:在编译时就确定了函数或方法的调用版本,主要通过函数重载和模板实现。
  • 运行时多态:在运行时才确定函数或方法的调用版本,主要通过虚函数和继承实现。
C++模板编程

C++ 模板编程是 C++ 语言的一个强大特性,它允许程序员编写与类型无关的代码。通过使用模板,可以编写出通用的函数和类,这些函数和类可以工作于多种数据类型上,而无需为每种数据类型都编写专门的代码。模板通常分为函数模板和类模板两种。

函数模板

函数模板允许你定义一个函数框架,该框架可以作用于多种数据类型上。模板的定义以 template <typename T>template <class T> 开头,其中 T 是一个占位符,代表将在函数调用时指定的数据类型。

#include <iostream>

// 函数模板定义
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    std::cout << "The max of 3 and 5 is " << max(3, 5) << std::endl; // 使用 int 类型
    std::cout << "The max of 3.14 and 2.71 is " << max(3.14, 2.71) << std::endl; // 使用 double 类型
    return 0;
}
类模板

类模板与函数模板类似,但它定义了一个类的框架,该类可以工作于多种数据类型上。通过类模板,可以创建出泛型的数据结构,如泛型容器、泛型算法等。

#include <iostream>

// 类模板定义
template <typename T>
class Box {
private:
    T m_value;

public:
    Box(T val) : m_value(val) {}

    void display() const {
        std::cout << "Box contains: " << m_value << std::endl;
    }
};

int main() {
    Box<int> intBox(10);
    intBox.display(); // 输出: Box contains: 10

    Box<double> doubleBox(3.14);
    doubleBox.display(); // 输出: Box contains: 3.14

    return 0;
}
模板特化

模板特化是模板编程中的一个高级特性,它允许你为模板的特定类型提供定制的实现。这在你需要为特定类型优化模板代码时非常有用。

template <typename T>
class Storage {
public:
    void store(T value) {
        // 通用实现
    }
};

// 特化 Storage 类以用于 char* 类型
template <>
class Storage<char*> {
public:
    void store(char* value) {
        // 为 char* 类型定制的实现
    }
};
如何禁止拷贝

从C++11开始,delete关键字被引入用于显式地删除函数,包括拷贝构造函数和拷贝赋值运算符。当你想要禁止拷贝时,可以将它们声明为delete

class NonCopyable {  
public:  
    NonCopyable() = default; // 默认构造函数  
    NonCopyable(const NonCopyable&) = delete; // 禁止拷贝构造  
    NonCopyable& operator=(const NonCopyable&) = delete; // 禁止拷贝赋值  
  
    // 可以提供移动构造函数和移动赋值运算符(如果需要)  
    NonCopyable(NonCopyable&&) = default; // 默认移动构造  
    NonCopyable& operator=(NonCopyable&&) = default; // 默认移动赋值  
  
    // 其他成员函数...  
};
拷贝构造函数

拷贝构造函数是C++中的一个特殊成员函数,它用于创建一个新对象作为另一个同类型对象的副本。当使用现有对象来初始化新对象时,会调用拷贝构造函数。拷贝构造函数的一个重要用途是确保资源(如动态分配的内存、文件句柄等)被正确地复制或管理。

  • 函数声明:
ClassName(const ClassName& other);
  • 自定义拷贝与深拷贝:为类定义自己的拷贝构造函数,以执行深拷贝
class MyClass {  
public:  
    int* data;  
  
    // 自定义拷贝构造函数  
    MyClass(const MyClass& other) {  
        // 分配新内存  
        data = new int(*other.data);  
        // 注意:如果 MyClass 有其他成员,也需要在这里进行复制  
    }  
  
    // 析构函数  
    ~MyClass() {  
        delete data;  
    }  
  
    // 禁用拷贝赋值运算符(可选,但推荐)  
    MyClass& operator=(const MyClass&) = delete;  
  
    // ... 其他成员函数 ...  
};
对象的实例化
  1. 例子:
#include <iostream>  
// 定义一个类  
class Point {  
public:  
    int x, y; // 成员变量  
    // 带参数的构造函数  
    Point(int xVal, int yVal) : x(xVal), y(yVal) {}  
    // 成员函数,用于打印点的坐标  
    void print() {  
        std::cout << "(" << x << ", " << y << ")" << std::endl;  
    }  
};  
  
int main() {  
    // 使用带参数的构造函数实例化对象  
    Point p2(10, 20);  
    p2.print(); // 输出: (10, 20)  
    return 0;  
}
  1. 过程:
  • 内存分配在实例化对象之前,需要为对象分配内存空间。根据对象的创建方式,内存可能分配在栈上或堆上。

  • 构造函数调用:在内存分配之后,会调用类的构造函数来初始化对象。构造函数是一个特殊的成员函数,它在对象创建时自动调用,用于设置对象的初始状态。

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

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

相关文章

Vue3Echarts写关于温湿度统计的好看折线图

在项目统计界面&#xff0c;我们离不开对Echarts的使用&#xff0c;接下来是我在做项目过程中做的一个关于温湿度统计的好看折线图&#xff0c;统计的是温度蓝色和湿度绿色&#xff0c;它们还会有告警和断电&#xff0c;分别用橘黄色和红色区分&#xff0c;以下是示例&#xff…

CesiumJS【Basic】- #056 绘制纹理填充多边形(Entity方式)-使用shader

文章目录 绘制纹理填充多边形(Entity方式)-使用shader1 目标2 代码2.1 main.ts绘制纹理填充多边形(Entity方式)-使用shader 1 目标 使用Entity方式绘制绘制纹理填充多边形 - 使用shader 2 代码 2.1 main.ts import * as Cesium from cesium;const viewer = new Cesium…

Linux系统中交叉编译opencv库

目标&#xff1a;将opencv进行交叉编译&#xff0c;使其能在rk3326板子上运行使用。 环境&#xff1a; ubuntu&#xff1a;18.04 opencv:4.5.4 opencv源码从挂网下载&#xff1a;opencv源码下载地址 交叉编译链&#xff1a;gcc-arm-10.3-linux-gun 一.环境准备 1.交叉编译链我…

【RT摩拳擦掌】如何构建RT AVB switchendpoint平台

【RT摩拳擦掌】如何构建RT AVB switch&endpoint平台 一&#xff0c;文档简介二&#xff0c;平台构建2.1 软硬件情况2.2 配置RT1170 AVB端点2.2.1 1块MIMXRT1170开发板做talker配置2.2.2 2块MIMXRT1170开发板做listener配置 2.3 AVB Switch 配置2.3.1 MOTU AVB Switch2.3.2 …

Ansys Zemax|场曲跟畸变图的前世今生

实现 OpticStudio通过在X和Y方向&#xff08;弧矢和子午方向&#xff09;的傍轴光线追踪确定近轴图像平面的Z坐标&#xff0c;并测量该近轴焦平面与系统图像平面的Z坐标之间的距离。 切向数据是沿Z轴从图像平面到近轴图像平面在切向&#xff08;YZ&#xff09;平面测量的距离…

【LeetCode刷题】3099.哈沙德数

题目链接 3099. 哈沙德数 - 力扣&#xff08;LeetCode&#xff09; 实现代码 int sumOfTheDigitsOfHarshadNumber(int x) {int sum 0;for(int temp x; temp; temp / 10)sum temp % 10;return x%sum ? -1 : sum; }

魔行观察-AI数据分析>>勒泰中心购物中心

摘要 本报告基于 魔行观察 搜集整理的数据&#xff0c;对勒泰中心购物中心的营业状态、商户构成、业态分布以及消费者评价进行了详细分析。 商场概览 勒泰中心是一个正常营业的购物中心&#xff0c;自2013年开业以来&#xff0c;已成为当地居民和游客的重要购物和休闲场所。…

3D一览通优化供应链协同,加速产品设计研发和上市

在现代企业管理中&#xff0c;供应链管理无疑占据着举足轻重的地位。它不仅是企业资源优化配置的基石&#xff0c;更是企业降低成本、提高效率、满足客户需求、保持市场竞争力的关键环节。对于工业企业来说&#xff0c;供应链的高效运作尤其重要。 然而&#xff0c;在实际操作…

一篇文章用python GUI构建学生管理系统

引言 通过使用Python&#xff0c;我们可以利用其简洁和功能强大的特性&#xff0c;结合Tkinter提供的GUI开发能力&#xff0c;快速构建一个直观且易于使用的学生管理系统。 准备工作 在开始之前&#xff0c;确保你的开发环境中已经安装了 PythonTkinter库 安装完成后&…

SQLServer:从数据类型 varchar 转换为 numeric 时出错。

1.工作要求 计算某两个经纬度距离 2.遇到问题 从数据类型 varchar 转换为 numeric 时出错。 3.解决问题 项目版本较老&#xff0c;使用SQLServer 2012 计算距离需执行视图&#xff0c;如下&#xff1a; SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO ALTER view vi_ord…

2024年迄今最热门的10款网络安全工具和产品

今年上半年&#xff0c;利用生成式人工智能&#xff08;GenAI&#xff09;的网络安全工具继续激增。许多供应商正在利用GenAI的功能来自动化安全运营中心&#xff08;SOC&#xff09;的工作&#xff0c;特别是在自动化日常活动方面&#xff0c;如收集威胁信息和自动创建查询。 …

Elasticsearch (1):ES基本概念和原理简单介绍

Elasticsearch&#xff08;简称 ES&#xff09;是一款基于 Apache Lucene 的分布式搜索和分析引擎。随着业务的发展&#xff0c;系统中的数据量不断增长&#xff0c;传统的关系型数据库在处理大量模糊查询时效率低下。因此&#xff0c;ES 作为一种高效、灵活和可扩展的全文检索…

Profibus DP主站转Modbus网关连接伺服与电机通讯

在工业自动化领域&#xff0c;将Profibus DP主站转Modbus网关&#xff08;XD-MDPBM20&#xff09;用于连接伺服与电机通讯是一种常见且重要的应用方式。当使用Profibus DP主站转Modbus网关&#xff08;XD-MDPBM20&#xff09;连接伺服与电机进行通讯时&#xff0c;可以参考以下…

强连通分量

强连通分量 强连通定义 有向图 G G G 的强连通是指 G G G 中任意两个节点都可以直接或间接到达。 下方两幅图都是强连通。一个特殊一点&#xff0c;任意两点都可以直接到达&#xff1b;一个则是最常见的强连通图。 特殊强连通图&#xff0c;任意两点都可以直接到达 常见的…

虚拟机启动失败 请进行修复 关闭hyper-v

场景 win11开启夜神模拟器时弹出此提示。点击关闭hyper-v并重启电脑后仍然不行。 解决方法 关闭 Windows安全中心 的 内存完整性 后重启电脑恢复正常。 补充 由于我这里除了会用到夜神模拟器&#xff0c;还会用到docker&#xff0c;而docker又依赖hyper-v&#xff0c;不…

YOLOv5初学者问题——用自己的模型预测图片不画框

如题&#xff0c;我在用自己的数据集训练权重模型的时候&#xff0c;在训练完成输出的yolov5-v5.0\runs\train\exp2目录下可以看到&#xff0c;在训练测试的时候是有输出描框的。 但是当我引用训练好的best.fangpt去进行预测的时候&#xff0c; 程序输出的图片并没有描框。根据…

【小白教学】-- 安装Ubuntu-20.04系统

下载 Ubuntu-20.04 镜像 具体如何下载镜像&#xff0c;请移驾我上一篇文章。使用清华大学开源镜像站下载。https://zhuanlan.zhihu.com/p/706444837 制作 Ubuntu-20.04 系统盘 安装软件 UltralSO 开始制作系统盘 第一步&#xff0c;插入一个 u 盘&#xff0c;启动软件&#x…

PO模式登录测试

项目实践 登陆项目测试 get_driver import page from selenium import webdriverclass GetDriver:driver Noneclassmethoddef get_driver(cls):if cls.driver is None:cls.driver webdriver.Edge()cls.driver.maximize_window()cls.driver.get(page.url)return cls.drivercl…

关于批量采集1688商品主图及链接的方式:软件采集/1688官方API接口数据采集

关于批量采集&#xff0c;我们通常用到的是软件 采集&#xff0c;或者通过1688官方API数据采集的形式&#xff1a;用户输入一组1688商品ID&#xff0c;一行一个&#xff0c;流程会自动逐个打开对应的1688商品详情页&#xff0c;采集主图的所有链接。 结果保存为表格的一行&…

Linux运维之管道符、重定向与环境变量

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、输入输出重定向 二、管道命令符 三、命令行的通配符 四、常用的转义字符 五、重要的环境变量 致谢 一、输入输出重定向 输入重定向是…