c++primier第十二章类和动态内存

news2024/11/19 16:35:38

本章内容包括:

  • 对类成员使用动态内存分配
  • 隐式和显式地复制构造函数
  • 隐式和显式地重载赋值操作符
  • 在构造函数中使用new所必须完成的工作
  • 使用静态类成员

  • 将布局new操作符用于对象
  • 使用指向对象的指针
  • 实现队列抽象数据类型(ADT)

 动态内存和类

复习范例和静态类成员

首先设计一个 StringBad类,然后设计一个功能稍强的 String类。

StringBad 和 String 类对象将包含一个字符串指针和一个表示字符串长度的值。这里使用 StingBad 和String 类,主要是为了深入了解 new、delete 和静态类成员的工作原理。因此,构造函数和析构函数调用时将显示一些消息,以便读者能够按照提示来完成操作。

对这个声明,需要注意的有两点。首先,它使用char指针(而不是char 数组)来表示姓名。这意味着类声明没有为字符串本身分配存储空间,而是在构造函数中使用new来为字符串分配空间。这避免了在类声明中预先定义字符串的长度。

strngbad.h

#include <iostream>
#ifndef STRNGBAD_H_
#define STRNGBAD_H_

class StringBad
{
private:
    char *str;
    int len;
    static int num_strings;

public:
    StringBad(const char *s);
    StringBad();
    ~StringBad(); // destructor// friend function
    friend std::ostream &operator<<(std::ostream &os, const StringBad &st);
};
#endif

strngbad.cpp

#include <cstring>
#include "strngbad.h"
using std::cout;

// initialjzing static class member
int StringBad::num_strings = 0;
// class methods
// construct StringBad fromC string
StringBad::StringBad(const char *s)
{
    len = std::strlen(s);
    str = new char[len + 1];
    std::strcpy(str, s);
    num_strings++;
    cout << num_strings << ": \" " << str << "\" object created\n";
}

StringBad::StringBad()
{
    len = 4;
    str = new char[4];
    std::strcpy(str, "C++");
    num_strings++;
    cout << num_strings << ": \"" << str << "\"default object created\n";
}
StringBad::~StringBad()
{
    cout << "\"" << str << "\" object deleted, ";
    --num_strings;
    cout << num_strings << "left\n";
    delete[] str;
}
std::ostream &operator<<(std::ostream &os, const StringBad &st)
{
    os << st.str;
    return os;
}

这条语句将静态成员num stings的值初始化为0。请注意,不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。

初始化是在方法文件中,而不是在类声明文件中进行的,这是因为类声明位于头文件中,程序可能将头文件包括在其他几个文件中。如果在头文件中进行初始化,将出现多个初始化语句副本,从而引发错误。

析构函数首先指出自己何时被调用。这部分包含了丰富的信息,但并不是必不可少的。不过,delete语句却是至关重要的。str 成员指向new 分配的内存。当StringBad 对象过期时,str 指针也将过期。但 str指向的内存仍被分配,除非使用deete 将其释放。删除对象可以释放对象本身占用的内存,但并不能自动释放属于对象成员的指针指向的内存。因此,必须使用析构函数。在析构函数中使用delete 语句可确保对象过期时,由构造函数使用 new分配的内存被释放。

vegnews.cpp

#include <iostream>
using std::cout;
#include "strngbad.h"

void callme1(StringBad &); // pass by reference
void callme2(StringBad);   // pass by value
int main()
{
    using std::endl;
    StringBad headlinel("Celery stalks at Midnight");
    StringBad headline2("Lettuce Prey");
    StringBad sports("Spinach Leaves Bowl for Dollars");
    cout << "headlinel:" << headlinel << endl;
    cout << "headline2:" << headline2 << endl;
    cout << "sports:" << sports << endl;
    callme1(headlinel);
    cout << "headlinel:" << headlinel << endl;
    callme2(headline2);
    cout << "headline2:" << headline2 << endl;
    cout << "Initialize one object to another:\n";
    StringBad sailor = sports;
    cout << "sailor:" << sailor << endl;
    cout << "Assign one object to another:\n";
    StringBad knot;
    knot = headlinel;
    cout << "knot: " << knot << endl;
    cout << " End of main()\n ";
    return 0;
}

void callme1(StringBad &rsb)
{
    cout << "String passed by reference: \n";
    cout << "    \"" << rsb << "\"\n";
}

void callme2(StringBad sb)
{
    cout << "String passed by value: \n";
    cout << sb << "\"\n";
}

提前中断

程序说明

callme1()按引用传递没有发生问题

callme2()按值传递发生问题

首先,将 headline2作为函数参数来传递从而导致析构函数被调用。其次,虽然按值传递可以防止原始参数被修改,但实际上函数已使原始字符串无法识别,导致显示一些非标准字符(显示的具体内存取决于内存中包含的内容)。

当您使用一个对象来初始化另一个对象时,编译器将自动生成上述构造函数(称为复制构造函数,因为它创建对象的一个副本)。自动生成的构造函数不知道需要更新静态变量num_strings,因此会将计数方案搞乱。实际上,这个例子说明的所有问题都是由编译器自动生成的成员函数引起的,下面介绍这主题。

除去所有赋值操作,结果显示正常。

隐式成员函数

StringBad 类中的问题是由自动定义的隐式成员函数引起的,这种函数的行为与类设计不符。

具体来说C++自动提供了下面这些成员函数:

  • 默认构造函数,如果没有定义构造函数
  • 复制构造函数,如果没有定义
  • 赋值操作符,如果没有定义
  • 默认析构函数,如果没有定义
  • 地址操作符,如果没有定义

 更准确地说,编译器将生成上述最后4个函数的定义---如果程序使用对象的方式要求这样做。例如,如果您将:个对象赋给另一个对象,编译器将提供赋值操作符的定义。

结果表明,StringBad 类中的问题是由隐式复制构造函数和隐式赋值操作符引起的。

默认构造函数

复制构造函数

复制构造函数的问题

注意:

 使用显式复制构造函数

解决类设计中这种问题的方法是进行深度复制(deepcopy)。

复制构造函数应当复制字符串并将副本的地址赋给s成员,而不仅仅是复制字符串地址。这样每个对象都有自己的字符串,而不是引用另一个对象的字符串。

调用析构函数时都将释放不同的字符串,而不会试图去释放已经被释放的字符串。

可以这样编写Sting的复制构造函数:

必须定义复制构造函数的原因在于,
一些类成员是使用new初始化的、指向数据的指针,而不是数据本身。

赋值操作符

C++允许类对象赋值,这是通过自动为类重载赋值操作符实现的。
这种操作符的原型如下:

赋值的问题 

解决赋值的问题

对于由于默认赋值操作符不合适而导致的问题,解决办法是提供赋值操作符(进行深度复制)定义其实现与复制构造函数相似,但也有一些差别

 代码首先检查自我复制,这是通过查看赋值操作符右边的地址(&s)是否与接收对象(this)的地址相同来完成的。如果相同,程序将返回*this,然后结束。

如果地址不同,函数将释放 str指向的内存,这是因为稍后将把一个新字符串的地址赋给 str。如果不首先使用 delete操作符,则上述字符串将保留在内存中。由于程序中不再包含指向该字符串的指针,因此这些内存被浪费掉。

改进后的新String类

头文件

// string1.h -- fixed and augmented string class definition
#include <iostream>
using std::istream;
using std::ostream;
#ifndef STRINGl_H_
#define STRINGI_H_
class String
{
private:
    char *str;
    int len;
    // pointer to string
    // length of string
    static int num_strings;       // number of objects
    static const int CINLIM = 80; // cin input limit
public:
    // constructors and other methods
    String(const char *s);  // constructor
    String();               //.default constructor
    String(const String &); // copy constructor
    ~String();
    // destructor
    int length() const { return len; }
    // overloaded operator methods
    String &operator=(const String &);
    String &operator=(const char *);
    char &operator[](int i);
    const char &operator[](int i) const;
    // overloaded operator friends
    friend bool operator<(const String &st, const String &st2);
    friend bool operator>(const String &stl, const String &st2);
    friend bool operator==(const String &st, const String &st2);
    friend ostream &operator<<(ostream &os, const String &st);
    friend istream &operator>>(istream &is, String &st); // static function
    static int HowMany();
};

#endif

方法文件

string1.cpp

// stringl.cppString class methods
#include <cstring>
#include "string1.h" //includes <iostream>
using std::cin;
using std::cout;

int String::num_strings = 0;
// static method
int String::HowMany()
{
    return num_strings;
}
// class methods
String::String(const char *s) // construct String fromC string
{
    len = std::strlen(s);
    // set size
    // allot storage
    str = new char[len + 1];
    std::strcpy(str, s);
    num_strings++;
}

String::String()
{
    len = 4;
    str = new char[1];
    str[0] = '\0';
    // default string
    num_strings++;
}

String::String(const String &st)
{
    num_strings++;
    len = st.len;
    str = new char[len + 1];
    std::strcpy(str, st.str);
}

String::~String()
{
    --num_strings;
    delete[] str;
}

String &String::operator=(const String &st)
{
    if (this == &st)
        return *this;
    delete[] str;
    len = st.len;
    str = new char[len + 1];
    std::strcpy(str, st.str);
    return *this;
}
// assign aC string to a string
String &String::operator=(const char *s)
{
    delete[] str;
    len = std::strlen(s);
    str = new char[len + 1];
    std::strcpy(str, s);
    return *this;
}

char &String::operator[](int i)
{
    return str[i];
}

// read-only char access for const String
const char &String::operator[](int i) const
{
    return str[i];
}
// overloaded operator friends
bool operator<(const String &st1, const String &st2)
{
    return (std::strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String &stl, const String &st2)
{
    return st2.str < stl.str;
}
bool operator==(const String &stl, const String &st2)
{
    return (std::strcmp(stl.str, st2.str) == 0);
}

// simple String output
ostream &operator<<(ostream &os, const String &st)
{
    os << st.str;
    return os;
}

// quick and dirty string input
istream &operator>>(istream &is, String &st)
{
    char temp[String::CINLIM];
    is.get(temp, String::CINLIM);
    if (is)
        st = temp;
    while (is && is.get() != '\n')
        continue;
    return is;
}

 程序文件

#include <iostream>
#include "string1.h"
const int ArSize = 10;
const int MaxLen = 81;
int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    String name;
    cout << "Hi,what's your name?\n>>";
    cin >> name;
    cout << name << ",please enter upto " << ArSize
         << "short sayings <empty line to quit>:\n";
    String sayings[ArSize]; // array of objects
    char temp[MaxLen];      // temporary string storage
    int i;
    for (i = 0; i < ArSize; i++)
    {
        cout << i + 1 << ": ";
        cin.get(temp, MaxLen);
        while (cin && cin.get() != '\n')
            continue;
        if (!cin || temp[0] == '\0') // empty line
            break;                   // i not incremented
        else
            sayings[i] = temp; // overloaded assignment
    }

    int total = i;
    // total # of lines read
    cout << "Here are your sayings:\n";
    for (i = 0; i < total; i++)
        cout << sayings[i][0] << ":" << sayings[i] << endl;

    int shortest = 0;
    int first = 0;
    for (i = 1; i < total; i++)
    {
        if (sayings[i].length() < sayings[shortest].length())
            shortest = i;
        if (sayings[i] < sayings[first])
            first = i;
    }
    cout << "shortest saying:\n"
         << sayings[shortest] << endl;
    cout << "First alphabetically:\n"
         << sayings[first] << endl;
    cout << "This program used " << String::HowMany()
         << "String objects. Bye.\n";
    return 0;
}

运行结果

在构造函数中使用new时应注意的事项

有关返回对象的说明


返回指向const对象的引用

使用const引用的常见原因是旨在提高效率,但对于何时可以采用这种方式存在一些限制。

如果函数返回(通过调用对象的方法或将对象作为参数)传递给它的对象,可以通过传递引用来提高方法的效率。例如,假设要编写函数 Max(),它返回两个 Vector 对象中较大的一个返回对象将调用复制构造函数,而返回引用不会。

引用指向的对象应该在调用函数执行时存在。

第三,v1和v2都被声明为 const引用,因此返回类型必须为const,这样才匹配。

返回非const对象的引用

cout 能连续输出;

返回对象

返回指向对象的指针

加入指针的程序

#include <iostream>
#include "string1.h"
#include <ctime>
const int ArSize = 10;
const int MaxLen = 81;
int main()
{
    using namespace std;
    String name;
    cout << "Hi,what's your name?\n>>";
    cin >> name;

    cout << name << ",please enter upto " << ArSize
         << "short sayings <empty line to quit>:\n";
    String sayings[ArSize]; // array of objects
    char temp[MaxLen];      // temporary string storage
    int i;
    for (i = 0; i < ArSize; i++)
    {
        cout << i + 1 << ": ";
        cin.get(temp, MaxLen);
        while (cin && cin.get() != '\n')
            continue;
        if (!cin || temp[0] == '\0') // empty line
            break;                   // i not incremented
        else
            sayings[i] = temp; // overloaded assignment
    }

    int total = i;
    // total # of lines read
    if (total > 0)
    {
        cout << "Here are your sayings:\n";
        for (i = 0; i < total; i++)
            cout << sayings[i] << endl;

        String *shortest = &sayings[0];
        String *first = &sayings[0];
        for (i = 1; i < total; i++)
        {
            if (sayings[i].length() < shortest->length())
                shortest = &sayings[i];
            if (sayings[i] < *first)
                first = &sayings[i];
        }
        cout << "shortest saying:\n"
             << *shortest << endl;
        cout << "First alphabetically:\n"
             << *first << endl;

        srand(time(0));
        int choice = rand() % total;
        String *favorite = new String(sayings[choice]);
        cout << "My favorite saying: \n " << *favorite << endl;
        delete favorite;
    }
    else
        cout << "No much to say, eh?\n";

    cout << "Bye.\n";
    return 0;
}

析构函数的调用

  • 如果对象是动态变量,则当执行完定义该对象的程序块时,将调用该对象的析构函数。
  • 如果对象是静态变量(外部、静态、静态外部或来自名称空间),则在程序结束时将调用对象的析构函数。
  • 如果对象是用new 创建的,则仅当您显式使用 delete 删除对象时,其析构函数才会被调用。

 指针和对象小结

再谈布局new操作符

#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
    string words;
    int number;

public:
    JustTesting(const string &s = "Just Testing", int n = 0)
    {
        words = s;
        number = n;
        cout << words << "constructed\n";
    }

    ~JustTesting() { cout << words << " destroyed\n"; }
    void Show() const
    {
        cout << words << ", " << number << endl;
    }
};

int main()
{
    char *buffer = new char[BUF];
    // get a block of memory
    JustTesting *pc1, *pc2;
    // place object in buffer
    pc1 = new (buffer) JustTesting;
    pc2 = new JustTesting("Heap1", 20); // place object on heap
    cout << "Memory block addresses:\n"
         << "buffer: " << (void *)buffer << "    heap : " << pc2 << endl;
    cout << " Memory contents :\n ";
    cout << pc1 << " : ";
    pc1->Show();
    cout << pc2 << " : ";
    pc2->Show();
    JustTesting *pc3, *pc4;
    pc3 = new (buffer) JustTesting("Bad Idea", 6);
    pc4 = new JustTesting("Heap2", 10);
    cout << "Memory contents:\n";
    cout << pc3 << ": ";
    pc3->Show();
    cout << pc4 << ": ";
    pc4->Show();

    delete pc2;
    delete pc4;
    delete[] buffer;
    cout << "Done\n";
    return 0;
}

在使用布局new操作符时存在两个问题。首先,在创建第二个对象时,布局new操作符使用一-个新对象来覆盖用于第一个对象的内存单元。显然,如果类动态地为其成员分配内存,这将引发问题。
其次,将 delete用于 pc2 和 pc4 时,将自动调用为 pc2 和 pc4 指向的对象调用析构函数;然而,将 delete[]用于 bufer 时,不会为使用布局 new 操作符创建的对象调用析构函数。

解决方法


 

原因在于 delete 可与常规 new 操作符配合使用,但不能与布局new操作符配合使用。

例如,指针 pc3没有收到 new 操作符返回的地址,因此 delete pc3 将导致运行阶段错误。在另一方面,指针 pc1指向的地址与 buffer 相同,但 buffer 是使用new[]初始化的,因此必须使用 delete[]而不是 delete 来释放。

即使 buffer是使用 new而不是new[]初始化的,delete pc1也将释放buffer,而不是 pc1。这是因为 new/delete 系统知道已分配的 512 字节块 buffer,但对布局new 操作符对该内存块做了何种处理一无所知。

#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
    string words;
    int number;

public:
    JustTesting(const string &s = "Just Testing", int n = 0)
    {
        words = s;
        number = n;
        cout << words << "constructed\n";
    }

    ~JustTesting() { cout << words << " destroyed\n"; }
    void Show() const
    {
        cout << words << ", " << number << endl;
    }
};

int main()
{
    char *buffer = new char[BUF];
    // get a block of memory
    JustTesting *pc1, *pc2;
    // place object in buffer
    pc1 = new (buffer) JustTesting;
    pc2 = new JustTesting("Heap1", 20); // place object on heap
    cout << "Memory block addresses:\n"
         << "buffer: " << (void *)buffer << "    heap : " << pc2 << endl;
    cout << " Memory contents :\n ";
    cout << pc1 << " : ";
    pc1->Show();
    cout << pc2 << " : ";
    pc2->Show();
    JustTesting *pc3, *pc4;
    pc3 = new (buffer + sizeof(JustTesting)) JustTesting("Bad Idea", 6);
    pc4 = new JustTesting("Heap2", 10);
    cout << "Memory contents:\n";
    cout << pc3 << ": ";
    pc3->Show();
    cout << pc4 << ": ";
    pc4->Show();

    delete pc2;
    delete pc4;

    pc3->~JustTesting();
    pc1->~JustTesting();
    delete[] buffer;
    cout << "Done\n";
    return 0;
}

 

复习各种技术

重载<<操作符

重新定义<<操作符,定义下面友元操作函数:

转换函数

其构造函数使用new的类

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

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

相关文章

ASP.NET Zero 多租户介绍

ASP.NET Zero 是一个基于 ASP.NET Core 的应用程序框架&#xff0c;它提供了多租户支持&#xff0c;以下是关于 ASP.NET Zero 多租户的介绍&#xff1a; 一、多租户概念 多租户是一种软件架构模式&#xff0c;允许多个客户&#xff08;租户&#xff09;共享同一套软件应用程序…

联邦学习(三只决策和大数据分析)(学习笔记)

联邦学习模型按照数据重叠形式分成了三类&#xff0c;分别是横向联邦学习、纵向联邦学习和联邦迁移学习。 横向联邦学习模型针对特征一致但ID不一致的数据&#xff1b; 纵向联邦学习模型针对ID一致但特征不一致的数据&#xff1b; 联邦迁移学习模型针对ID和特征都不一致的数据。…

Linux网络操作命令与函数全面总结

1. 引言 Linux作为服务器和开发平台&#xff0c;网络操作是其核心功能之一。本文旨在全面总结Linux系统中的网络操作方法&#xff0c;包括命令行工具和编程接口&#xff0c;帮助读者深入理解Linux网络管理的机制。 2. 命令行工具 2.1 ping 命令 ping 命令用于测试网络连接和…

实验OSPF路由协议(课内实验)

实验1&#xff1a;OSPF路由协议 实验目的及要求&#xff1a; 通过实验&#xff0c;能够理解链路状态型路由协议OSPF协议的工作原理&#xff0c;掌握如何实现单区域 OSPFv2配置指令&#xff0c;能够熟练的应用各种OSPF协议相关的配置指令完善网络设计。掌握验证OSPFv2网络连接…

软件开发人员绩效考核方案(参考)

1、产品&运营绩效考核表 2、开发绩效考核表 3、测试绩效考核表 4、CPI指标库 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查…

像AI一样思考

我感觉每个人都应该通过Coze平台 https://www.coze.cn/ 创建一个属于自己的Bot。 为什么要创建Bot&#xff1f; 很多时候我们的思维会受各种因素的影响&#xff0c;如感情、知识盲区、心态等&#xff0c;最终导致我们做的决定缺乏逻辑或者考虑不全面。但AI不一样&#xff0c;…

armbian安装docker

最近又搞了台瑞莎Radxa 3E &#xff0c;从零开始部署unbuntu环境&#xff0c;发现是真曲折啊&#xff0c;虽然有点前车之鉴了 在Armbian上安装Docker&#xff0c;可以按照以下步骤操作&#xff1a; 1、更新软件包列表&#xff1a; sudo apt-get update 2、安装必要的软件包…

leetcode|刷算法 线段树原理以及模板

线段树出现的题目特征 线段树使用的题目。每次操作都要得到返回结果的。 比如 699. 掉落的方块 - 力扣&#xff08;LeetCode&#xff09; 2286. 以组为单位订音乐会的门票 - 力扣&#xff08;LeetCode&#xff09; 1845. 座位预约管理系统 - 力扣&#xff08;LeetCode&#…

【韩顺平Java笔记】第4章:运算符

文章目录 61. 上一章总结62. 算术运算符介绍62.1 运算符介绍62.2 算术运算符介绍62.3 算术运算符一览 63. 算术运算符使用64. 算术运算符练习165. 算术运算符练习266. 67. 算术运算符练习3,468. 关系运算符介绍68.1 关系运算符介绍68.2 关系运算符一览 69. 关系运算符使用70. 逻…

2024暄桐“静定的滋养”|静坐篇之林曦老师聊静坐

我们都喜爱“静”&#xff0c;它是一种因能量充足而带来的稳定放松的状态。 正在报名中的暄桐《2024书法课程 第五阶“静定的滋养” | 从书法之美到生活之美——林曦老师的线上直播书法课》&#xff0c;除了书法进阶部分的内容之外&#xff0c;读书部分正是帮助我们加强对静定的…

Windows应急响应-PcShare远控木马

文章目录 应急背景木马查杀1.查看异常连接2.查看进程3.查看服务定位到注册表 开始查杀 入侵排查1.账户排查2.开机自启3.服务4.计划任务5.网络情况6.进程排查重启再排查一遍 应急背景 曲某今天想要装一款软件&#xff0c;通过网上搜索看到非官方网站进入后直接下载下来后进行安…

毕业设计选题:基于ssm+vue+uniapp的购物系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

DSPy101

DSPy 介绍 DSPy&#xff08;Declarative Self-improved Language Programs in Python&#xff09; 是一个用于系统化和增强在流水线内使用语言模型的框架&#xff0c;它通过数据驱动和意图驱动的系统来优化大型语言模型&#xff08;LLM&#xff09;的使用。 DSPy 的核心是模块…

我的笔记本电脑之前可以直接用音量键调节音量,后来需要fn键加音量键才能调节,这是为什么?

我的笔记本电脑之前可以直接用音量键调节音量&#xff0c;后来需要fn键加音量键才能调节&#xff0c;这是为什么&#xff1f; 直接按 FnEsc就能解除Fn的锁定

信息安全数学基础(24)模为奇数的平方剩余与平方非剩余

前言 在信息安全数学基础中&#xff0c;模为奇数的平方剩余与平方非剩余是数论中的一个重要概念&#xff0c;特别是在密码学和安全协议中扮演着关键角色。当模数为奇数时&#xff0c;我们通常关注的是模为奇素数的平方剩余与平方非剩余&#xff0c;因为奇合数的情况更为复杂且…

【数学分析笔记】第4章第2节 导数的意义和性质(2)

4. 微分 4.2 导数的意义与性质 4.2.3 单侧导数 f ′ ( x ) lim ⁡ Δ x → 0 f ( x Δ x ) − f ( x ) Δ x lim ⁡ x → x 0 f ( x ) − f ( x 0 ) x − x 0 f(x)\lim\limits_{\Delta x\to 0}\frac{f(x\Delta x)-f(x)}{\Delta x}\lim\limits_{x\to x_0}\frac{f(x)-f(x_0)…

Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; func findDisappearedNumbers(nums []int) (ans []int) {n : len(nums)for _, v : range nums {v (v - 1) % nnums[v] n}for i, v : range nums {if v < n {ans append(ans, i1)}}return }

OceanBase企业级分布式关系数据库

简介 OceanBase 数据库是阿里巴巴和蚂蚁集团不基于任何开源产品&#xff0c;完全自研的原生分布式关系数据库软件&#xff0c;在普通硬件上实现金融级高可用&#xff0c;首创“三地五中心”城市级故障自动无损容灾新标准&#xff0c;具备卓越的水平扩展能力&#xff0c;全球首…

使用微服务Spring Cloud集成Kafka实现异步通信(消费者)

1、本文架构 本文目标是使用微服务Spring Cloud集成Kafka实现异步通信。其中Kafka Server部署在Ubuntu虚拟机上&#xff0c;微服务部署在Windows 11系统上&#xff0c;Kafka Producer微服务和Kafka Consumer微服务分别注册到Eureka注册中心。Kafka Producer和Kafka Consumer之…

Ajax ( 是什么、URL、axios、HTTP、快速收集表单 )Day01

AJAX 一、Ajax是什么1.1名词解释1.1.1 服务器1.1.2 同步与异步1. 同步&#xff08;Synchronous&#xff09;2. 异步&#xff08;Asynchronous&#xff09;3. 异步 vs 同步 场景4. 异步在 Web 开发中的常见应用&#xff1a; 1.2 URL 统一资源定位符1.2.1 URL - 查询参数1.2.2 ax…