C++课程学习记录

news2024/11/23 13:06:49

目录

    • 1. 前置说明
    • 2. 二叉树的模拟
      • 2.1 参考资料
      • 2.2 二叉树的构建
        • 2.2.1 递归构建
        • 2.2.2 迭代构建
      • 2.3 二叉树的遍历
      • 2.4 二叉树的应用
    • 3. 继承与派生
      • 3.1 最简单的生死
      • 3.2 动态申请空间的生死
      • 3.3 继承中的protectd权限
      • 3.4 三种继承方式
      • 3.5 修改某些继承成员的继承类型
      • 3.6 多级派生
      • 3.7 多重继承
        • 3.7.1 变量域覆盖
        • 3.7.2 使用域作用运算符分辨成员
      • 3.8 虚基类
      • 3.9 基类和派生类的转换
    • 4. 多态性与虚函数
      • 4.1 前言
      • 4.2 虚函数
      • 4.3 纯虚函数
      • 4.4 抽象类

1. 前置说明

这篇博客是用来记录期中考试过后我对C++的课上复现以及课后练习的过程

  • 知识点请食用目录进行参考
  • 未涉及的知识点请食用我在之前写过の一篇期中机考总结

传送门:C++期中机考试题


2. 二叉树的模拟

2.1 参考资料

  • 先贴上这一篇对我影响颇深的博客,没有他我怎么都想不明白二叉树的三种遍历方式
    数据结构——二叉树先序、中序、后序及层次四种遍历(C语言版)
  • 由于二叉树的构建其中一种涉及到了递归建树,我再贴一篇博客,以此记录这篇博客对我递归理解的启发与深远影响
    100天精通Python(基础篇)——第10天:函数进阶
  • 最后再贴一篇,除了课上听的与结合chat问出来的,这篇博客写的也很详细,尤其是代码部分比较符合我的习惯,也算帮了大忙。并且,由于以下我只涉及到了二叉树的构建与遍历,其他的包括删除结点与查询结点并未涉及,大家可以按照需要进行补充
    二叉排序树(二叉查找树、二叉搜索树)(图解+完整代码)

2.2 二叉树的构建

2.2.1 递归构建

#include <iostream>

using namespace std;

struct Node
{
    int val;
    Node* left;
    Node* right;

    // 创建新结点时,同时重置左右结点为空结点
    Node(int _val) : val(_val), left(nullptr), right(nullptr) {}
};

class BinaryTree
{
private:
    Node* root;

    // 找到适合插入的位置并创建结点,返回所创建结点的地址
    Node* Insert(Node* now, int x)
    {
        if (now == nullptr)
            now = new Node(x);
        else if (x < now->val)
            now->left = Insert(now->left, x);
        else
            now->right = Insert(now->right, x);

        return now;
    }

    void Output(Node* now)
    {
        if (now == nullptr) return;

        Output(now->left);
        cout << now->val << ' ';
        Output(now->right);
    }

public:
    BinaryTree() : root(nullptr) {}

    // 递归构建二叉树
    void Insert(int x)
    {
        root = Insert(root, x);
    }

    // 保护私有数据root
    void Output()
    {
        Output(root);
    }
};

int main()
{
    BinaryTree Tree;

    int a[] = {3, 2, 4, 1, 5, 7, 9, 6, 8}, n = 9;

    for (int i = 0; i < n; i++)
        Tree.Insert(a[i]);

    Tree.Output();

    return 0;
}

2.2.2 迭代构建

#include <iostream>

using namespace std;

struct Node
{
    int val;
    Node* left;
    Node* right;

    // 创建新结点时,同时重置左右结点为空结点
    Node(int _val) : val(_val), left(nullptr), right(nullptr) {}
};

class BinaryTree
{
private:
    Node* root;

    void Output(Node* node)
    {
        if (node == nullptr) return;

        Output(node->left);
        cout << node->val << ' ';
        Output(node->right);
    }

public:
    BinaryTree() : root(nullptr) {}

    // 迭代构建二叉树
    void Insert(int x)
    {
        if (root == nullptr)
        {
            root = new Node(x);
            return;
        }
        Node* cur = root;
        while (true)
        {
            if (x == cur->val) break;
            else if (x < cur->val)
            {
                if (cur->left == nullptr)
                {
                    cur->left = new Node(x);
                    break;
                }
                cur = cur->left;
            }
            else
            {
                if (cur->right == nullptr)
                {
                    cur->right = new Node(x);
                    break;
                }
                cur = cur->right;
            }
        }
    }

    // 保护私有数据root
    void Output()
    {
        Output(root);
    }
};

int main()
{
    BinaryTree Tree;

    int a[] = {3, 2, 4, 1, 5, 7, 9, 6, 8}, n = 9;

    for (int i = 0; i < n; i++)
        Tree.Insert(a[i]);

    Tree.Output();

    return 0;
}

三种输出结果:

前序遍历:3, 2, 1, 4, 5, 7, 6, 9, 8
中序遍历:1, 2, 3, 4, 5, 6, 7, 8, 9
后序遍历:1, 2, 6, 8, 9, 7, 5, 4, 3

2.3 二叉树的遍历

伪代码:(中序遍历为例)
---------------------
传入结点
if (结点为空) return;

传入结点的左指针;
输出;
传入结点的右指针;

2.4 二叉树的应用

赠送一道算法题
AcWing 3384. 二叉树遍历

AC代码

#include <iostream>

using namespace std;

string s;
int pos;

struct Node
{
    char val;
    Node* left;
    Node* right;

    Node(char x) : val(x), left(nullptr), right(nullptr) {}
};

class BinaryTree
{
private:
    Node* root;

    Node* build()
    {
        char c = s[pos++];
        if (c == '#') return nullptr;

        Node* node = new Node(c);
        node->left = build();
        node->right = build();

        return node;
    }

    void OutputIn(Node* now)
    {
        if (now == nullptr) return;

        OutputIn(now->left);
        cout << now->val << ' ';
        OutputIn(now->right);
    }

public:
    BinaryTree() : root(nullptr) {}

    void buildTree()
    {
        root = build();
    }

    void OutputIn()
    {
        OutputIn(root);
    }
};

int main()
{
    BinaryTree Tree;

    cin >> s;

    Tree.buildTree();

    Tree.OutputIn();

    return 0;
}

3. 继承与派生

参考书籍:《C++面向对象程序设计》 ——科学出版社

3.1 最简单的生死

#include <iostream>
using namespace std;

class A
{
private:
	int x;
public:
	A(int x = 0) : x(x)
	{
		cout << x << endl;
	}
	virtual ~A()
	{
		cout << -x << endl;
	}
};

class AA :public A
{
private:
	int xx;
public:
	AA(int x, int xx) : A(x), xx(xx)
	{
		cout << xx << endl;
	}
	~AA()
	{
		cout << -xx << endl;
	}
};

int main()
{
	AA a1(3, 4);
	AA a2(2, 5);
	return 0;
}
输出:
3
4
2
5
-5
-2
-4
-3
  • 列表化初始化的时候感觉和父类这个对象是当前子类的成员变量一样
  • 析构顺序为:父生,子生,子死,父死

3.2 动态申请空间的生死

类与上述一致

int main()
{
	A* a = new A(2);
	AA* aa = new AA(3, 4);
	delete a;
	delete aa;
	return 0;
}
  • 这没啥好说的想谁生就生,想谁析构就让谁析构
  • 父类的使用与平常一致,没有影响
  • 有一个好玩的地方就是关于析构函数中virtual关键字是否使用的问题。如果当前类可继承,并且确实被继承了,那么它的析构函数就一定要加virtual关键字,这样才能确保内存释放干净
  • 可以将基类与派生类之间的继承关系理解为一棵多叉树,其中最开始被继承的基类为根节点。假设此时有一个例子为:利用一个A类的指针创建一个B类的对象A* ptr = new B,那么
    • 创建过程:
      1. 从B结点开始递归搜索到A结点
      2. 从A结点开始依次执行到B结点
    • 销毁过程:
      1. ptr所属对象A的结点开始递归搜索到B结点
      2. 从B结点开始依次向A结点销毁

3.3 继承中的protectd权限

#include<iostream> 
using namespace std;

class A
{
private:
	int x1;  // 完全是自己控制
public:
	int x2;  // 完全供大家控制
protected:   // 传给子类
	int x3;
public:
	A(int x1, int x2, int x3) :x1(x1), x2(x2), x3(x3)
	{
		cout << x1 << " " << x2 << " " << x3 << endl;
	}
	virtual ~A()
	{
		cout << -x1 << " " << -x2 << " " << -x3 << endl;
	}
};
class AA : public A
{
	int xx;
public:
	AA(int x1, int x2, int x3, int xx) :A(x1, x2, x3), xx(xx)
	{
		cout << x1 << " " << x2 << " " << x3 << " " << xx << endl;
	}
	~AA()
	{
		// cout<<-x1  不能存取父类中的私有成员
		cout << -x2 << " " << -x3 << " " << -xx << endl;
	}
};

int main()
{
	A* p1 = new  A(1, 2, 3);
	delete p1;

	AA* p2 = new AA(4, 5, 6, 7); // 此程序等价于A* p2 = new AA(4, 5, 6, 7);
	delete p2;
	
	return 0;
}
输出:
1 2 3
-1 -2 -3
4 5 6
4 5 6 7
-5 -6 -7
-4 -5 -6
  • protected的数据成员只可以在这个类的派生类中使用,其他地方都不可以调用
  • 基类的private数据成员只可以在这个类中使用,其他地方包括它的派生类都不可以访问

3.4 三种继承方式

  • 公有继承

    就是没啥区别,这是主要工程上主要使用的继承方式

  • 私有继承

    可以理解为

    • 将基类的公有与保护的成员全部变成私有类型了,类外都不能访问到基类的public成员了
    • 将基类的公有与保护的成员作为自己的私有成员继承下来了,后续的派生类都没有访问的权限
  • 保护继承
    • 可以理解为将基类的公有的成员全部变成保护类型了,类外同样都不能访问到基类的public成员了
    • 将基类的公有成员作为自己的保护成员继承下来了,后续的派生类任然可以访问将基类的public成员作为protected成员进行访问

3.5 修改某些继承成员的继承类型

只是在派生类中对继承下来的成员进行了继承方式的修改,并不影响基类中成员本身的权限属性

举个例子

#include <iostream>
#include <cstring>
using namespace std;

// 在派生类中将基类的某些特点成员重继承为与派生类的继承方式不同的成员

class A
{
private:
protected:
public:
	void f1()
	{
		cout << "默认私有继承后,外界无法访问到基类中的公有成员了" << "\n";
	}
	void f2()
	{
		cout << "我让这个普通函数变为外界可访问" << "\n";
	}
	void f3()
	{
		cout << "我让这个函数变为外界可访问" << "\n";
	}
	void f3(int)
	{
		cout << "我让这个重载函数通过一句话也变为外界可访问" << "\n";
	}
};

class B :A // 默认私有继承
{
private:
protected:
public:
	A::f2;
	A::f3; // 这一句话就可以改变所有的同名的重载函数
};

int main()
{
	B object;

//	object.f1(); “A::f1”不可访问,因为“B”使用“private”从“A”继承
	object.f2();
	object.f3();
	object.f3(1);

	return 0;
}

再举个例子

#include <iostream>
#include <cstring>
using namespace std;

// 在派生类中将基类的某些特点成员重继承为与派生类的继承方式不同的成员

class A
{
private:
protected:
public:
	void f1()
	{
		cout << "默认私有继承后,外界无法访问到基类中的公有成员了" << "\n";
	}
	void f2()
	{
		cout << "我让这个普通函数变为外界可访问" << "\n";
	}
	void f3()
	{
		cout << "我让这个函数变为外界可访问" << "\n";
	}
	void f3(int)
	{
		cout << "我让这个重载函数通过一句话也变为外界可访问" << "\n";
	}
};

class B :A // 默认私有继承
{
private:
protected:
public:
	A::f2;
	// 这样写就可以改变特定的重载函数的开放权限了
	void f3()
	{
		A::f3();
	}
};

int main()
{
	B object;

//	object.f1(); “A::f1”不可访问,因为“B”使用“private”从“A”继承
	object.f2();
	object.f3();
//	object.f3(1);“B::f3”: 函数不接受 1 个参数

	return 0;
}

3.6 多级派生

没什么好多说的,上代码!代码看懂了,输出结果看懂了,就OK了
唯一需要强调的一点是,在多级派生时,对于一个派生类对象的初始化,一定要附带着把他所有的“爹”也全部初始化,否则调用默认构造函数初始化他的所有“爹”

#include <iostream>
#include <string>
using namespace std;

class A
{
protected:
	int id;
	string name;
public:
	A(int _id = 0, string _name = "hehe") : id(_id), name(_name) 
	{
		cout << "基类A被创建" << endl;
	}
	~A() 
	{ 
		cout << "基类A被销毁" << endl; 
	}
	int get_id() { return id; }
	string get_name() { return name; }
	void show_A()
	{
		cout << "id: " << "\t" << get_id() << endl;
		cout << "name: " << "\t" << get_name() << endl;
	}
};

class B : public A
{
protected:
	double score;
public:
	B(int _id = 0, string _name = "hehe", double _score = 99.5)
		: A(_id, _name), score(_score)
	{
		cout << "直接派生类B被创建" << endl;
	}
	virtual ~B()
	{
		cout << "直接派生类B被销毁" << endl;
	}
	double get_score() { return score; }
	void show_B()
	{
		show_A();
		cout << "score: " << "\t" << get_score() << endl;
	}
};

class C : public B
{
protected:
	int age;
public:
	C(int _id = 0, string _name = "hehe", double _score = 99.5, int _age = 18)
		: B(_id, _name, _score), age(_age)
	{
		cout << "间接派生类C被创建" << endl;
	}
	virtual ~C()
	{
		cout << "间接派生类C被销毁" << endl;
	}
	int get_age() { return age; }
	void show_C()
	{
		show_B(); 
		cout << "age: " << "\t" << get_age() << endl;
	}
};

void Test()
{
	C obj(1, "dwj", 100.0, 16);
	obj.show_C();	
}

int main()
{
	Test();
	return 0;
}
// 输出结果
基类A被创建
直接派生类B被创建
间接派生类C被创建
id:     1
name:   dwj
score:  100
age:    16
间接派生类C被销毁
直接派生类B被销毁
基类A被销毁

由于是公有继承,故在C类创建的对象中,可以调用父类的全部public以及protected方法,当然自己的什么都可以调用啦~
展示图片

3.7 多重继承

也就是一个派生类继承了 ≥ 2 \ge2 2个基类

3.7.1 变量域覆盖

当前对象的成员如果与基类的成员重名或者函数重名并且参数列表完全一致,就会被当前的派生类完全覆盖,以下为变量名覆盖

程序

#include <iostream>
#include <string>
using namespace std;

class A
{
protected:
    string name;
public:
    A(string _name) : name(_name) {}
};

class B
{
protected:
    string name;
public:
    B(string _name) : name(_name) {}
};

class C : public A, public B
{
private:
    string name;
public:
    C(string _name_A, string _name_B, string _name_C)
    : A(_name_A), B(_name_B), name(_name_C) {}
    void show()
    {
        cout  << "A的name: " << name << endl;
        cout  << "B的name: " << name << endl;
        cout  << "C的name: " << name << endl;
    }
};

int main()
{
    C test("Tom", "Jerry", "John");

    test.show();

    return 0;
}

输出结果

A的name: John
B的name: John
C的name: John

Process finished with exit code 0

3.7.2 使用域作用运算符分辨成员

修改后的类C

class C : public A, public B
{
public:
    C(string _name_A, string _name_B)
    : A(_name_A), B(_name_B) {}
    void show()
    {
        cout  << "A的name: " << name << endl;
        cout  << "B的name: " << name << endl;
    }
};

运行后错误提示为

error: reference to 'name' is ambiguous

修改类C

class C : public A, public B
{
public:
    C(string _name_A, string _name_B)
    : A(_name_A), B(_name_B) {}
    void show()
    {
        cout  << "A的name: " << A::name << endl;
        cout  << "B的name: " << B::name << endl;
    }
};

运行结果

A的name: Tom
B的name: Jerry

Process finished with exit code 0

3.8 虚基类

无论虚基类出现在继承层次的哪个位置上,他们都是在非虚基类之前被构造。有虚基类的派生类的构造函数的调用次序是:编译器按照直接基类的声明次序,检查虚基类的出现情况,每个继承子树按照深度优先的顺序检查并调用虚基类的构造函数。虚基类多次出现只调用一次构造函数。虚基类的构造函数调用完之后,再按照声明的顺序调用非虚基类的构造函数(真的是又臭又长😢)

总结一下就是两点:

  1. 按照声明顺序,深度优先搜索虚基类,后序构造
  2. 按照声明顺序,深度优先搜索非虚基类,后序构造

举三个例子就知道了(图中虚继承用虚线表示)


例一:

class A { };
class B : public virtual A { }; 
class C : public virtual A { };
class D : public B, public C { };

在这里插入图片描述

那么D对象创建时,构造函数的调用顺序是:A, B, C, D


例二:

class A { };
class B { };
class C : public B, public virtual A { };
class D : public virtual A { };
class E : public C, public virtual D { };

在这里插入图片描述

那么E对象创建时,构造函数的调用顺序是:A, D, B, C, E


例三:

class A {};
class B : public A {};
class C {};
class D {};
class E : public virtual D {};
class F : public B, public E, public virtual C {};

在这里插入图片描述

那么F对象创建时,构造函数的调用顺序是:D, C, A, B, E, F


3.9 基类和派生类的转换

大概就是对于一个派生类,在赋值 or 指针传递 or 引用传递给一个基类对象 or 基类指针 or 基类引用时,会失去派生类的另外加的成员变量,变成一个基类的对象。为了通过基类的指针,改变派生类的成员方法,引出了C++中的一大特性:多态性

4. 多态性与虚函数

4.1 前言

先补充一下函数调用绑定的知识点:分为静态绑定与动态绑定

静态绑定:函数与函数体在编译的时候就对应捆绑起来了,比如重载

动态绑定:按照主观意愿,使用一个函数名动态的调用很多同名的函数

4.2 虚函数

其实就是在父类中的函数声明最前面加上virtual关键词,从而在子类中可以重名改写这个函数,最后在相应的对象中通过指针或者引用调用响应对象的相同的函数,实现不同的自定义功能

  • 举个例子
    #include <iostream>
    using namespace std;
    
    class A
    {
    protected:
        int a;
    public:
        A() { a = 0; }
        virtual void Output()
        {
            cout << "A: " << a << endl;
        }
    };
    
    class B : public A
    {
    private:
        int b;
    public:
        B() : A() { b = 1; }
        void Output()
        {
            cout << "A: " << a << endl;
            cout << "B: " << b << endl;
        }
    };
    
    int main()
    {
        A obj_A;
        A *pA = &obj_A;
        pA->Output();
    
        B obj_B;
        pA = &obj_B;
        pA->Output();
    
        return 0;
    }
    
  • 输出
    在这里插入图片描述
  • 如果删除基类中的virtual关键词,则无法调用子类对象中的Output函数,只会输出这个
    在这里插入图片描述
  • 并且会警告
    在这里插入图片描述

4.3 纯虚函数

其实就是提供一个接口,只有函数声明,不写任何函数定义,从而实现了一个从基类定义的接口,供所有的子类进行函数的重写以及后续主函数中的调用(通过指针)

唯一的语法注意点就是加一个=0,如下是一个示例

class A
{
private:
public:
    virtual void f() = 0;
    virtual void g();
    void h();
};
  • f()是一个纯虚函数,用来做基类中的接口的
  • g()是一个虚函数,需要有自己的定义
  • h()是一个普通的基类中的成员函数,在其派生类中不可以进行重写

4.4 抽象类

包含纯虚函数的类就是一个抽象类;如果类中全是纯虚函数,则这个类就是纯抽象类,如下是一个示例

class A
{
private:
public:
    virtual void f() = 0;
    virtual void g() = 0;
    virtual void h() = 0;
};

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

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

相关文章

C++57个入门知识点_番外1_C++指针偏移在类中的应用及指针偏移原理

这是对C指针偏移介绍比较好的博文&#xff0c;但是比较分散&#xff0c;我把其进行了整理&#xff0c;原博文地址请见最后&#xff0c;讲的很详细。 C57个入门知识点_番外1_C指针偏移在类中的应用及指针偏移原理 1. C指针偏移原理2. C显示十进制内存地址&#xff08;不用理解&…

AQS原理

目录 一、原理概述二、AQS 对资源的共享方式三、AQS底层使用了模板方法模式四、使用demo&#xff0c;使用AQS实现不可重入锁五、AQS使用到的几个框架 一、原理概述 AQS全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架 AQS核心思想是&#…

Appian低代码平台

国外老牌低代码开发平台Appian Appian在国内用的比较少&#xff0c;资料也很匮乏。需要自己主动去官网寻找。 Appian 学习平台 进入Appian Community可以选择学习路径&#xff0c;可以选择适合自己的学习路径&#xff1b;我选择的是Builder路径&#xff0c; 看了足足80个小…

opencv检测二维码和条形码

文章目录 1 excel制作简单二维码2 识别二维码和条形码2.1 相关库2.2 decode解码2.3 圈出二维码的位置2.4 判断二维码是否授权 3 完整代码3.1 使用图片进行识别3.2 使用摄像头实时识别 4 总结 1 excel制作简单二维码 使用excel可以实现制作二维码&#xff0c;但只能实现做英文和…

基于51单片机的简易电子琴设计

目录 摘 要 基于51单片机的简易电子琴设计 一、系统设计 1、项目概要 2.设计任务和基本要求 二、硬件设计 1、硬件设计概要 2、时钟振荡电路模块 3.复位电路模块 5.数码管电路模块 6.蜂鸣器模块 7、乐曲切换电路模块 三、软件原理 四、软件流程图 五、代码实现 …

解析Transformer基本结构与实现

1.基本结构 ​ Transformer总体架构可分为4个部分&#xff1a; 输入部分-输出部分-编码器部分-解码器部分 输入部分包含&#xff1a; 原文本嵌入层&#xff08;Input embedding&#xff09;及其位置编码(position encoding)目标文本嵌入层及其位置编码器 文本嵌入层的作…

TCP/UDP协议重温三次握手四次挥手 简单笔记

术语储备&#xff1a; SYN&#xff1a;同步位 &#xff1b;SYN1,表示进行一个连接请求 ACK&#xff1a;确认位 &#xff1b;ACK1,确认有效 ACK0&#xff0c;确认无效 ack : 确认号 &#xff1b;对方发送序号1 seq &#xff1a; 序号 ; 标识从TCP发端向TCP收端发送的数据字节流 …

基于JPA的Repository使用详解

Spring Data JPA Spring Data是Spring提供的操作数据的框架&#xff0c;Spring Data JPA是Spring Data的一个模块&#xff0c;通过Spring data 基于jpa标准操作数据的模块。 Spring Data的核心能力&#xff0c;就是基于JPA操作数据&#xff0c;并且可以简化操作持久层的代码。…

BLOND:ISH VoxEdit 创作大赛来啦!

准备好随着 BLOND:ISH 的节拍释放你们的创造力和节奏&#xff0c;因为我们将举办一场与众不同的刺激比赛。你们可以在 BLOND:ISH VoxEdit 大赛中展示你们的才华并赢得 SAND 奖励&#xff01; &#x1f3dd;️ 比赛主题&#xff1a;ABRA 夏日派对 &#x1f3dd;️ 释放你们的想象…

【Python学习】—Python基础语法(二)

文章目录 【Python学习】—Python基础语法&#xff08;二&#xff09;一、字面量二、注释三、变量四、数据类型五、数据类型转换六、标识符七、运算符八、字符串扩展九、获取键盘输入 【Python学习】—Python基础语法&#xff08;二&#xff09; 一、字面量 字面量&#xff1…

文件类型识别的实现思路

一些网络设备&#xff0c;比如防火墙或者审计系统&#xff0c;一般都有文件过滤的功能&#xff0c;可以对用户上网传输的文件进行过滤&#xff0c;比如可以限制用户通过ftp下载word文档&#xff0c;也就是文件类型为doc或者docx的文件。 那么文件过滤的功能是怎么实现呢&#…

2023年计算机专业还值得无脑梭哈吗——计算机类专业在读一年学生个人分享

长忆观潮&#xff0c;满郭人争江上望。来疑沧海尽成空&#xff0c;万面鼓声中。 弄潮儿向涛头立&#xff0c;手把红旗旗不湿。别来几向梦中看&#xff0c;梦觉尚心寒。 IT 界每隔几年就有一波浪潮或者泡沫&#xff0c;新的一波大潮已经打过来了&#xff0c;躲&#xff1f;能跑多…

【计算机视觉】CVPR 23 新论文 | 异常检测最新改进方法:DeSTSeg

文章目录 一、导读二、背景2.1 主要贡献2.2 网络介绍&#xff1a;DeSTSeg 三、方法3.1 Synthetic Anomaly Generation 合成异常生成3.2 Denoising Student-Teacher Network 去噪教师学生网络3.3 Segmentation Network 分割网络 四、实验结果 一、导读 DeSTSeg: Segmentation G…

数字图像处理【10】直方图反向投影与模板匹配

本篇简单描述直方图在图像处理中典型的应用场景&#xff0c;属于是比较老旧的应用技术&#xff0c;但不妨拿出来再学习&#xff0c;温故而知新&#xff0c;为新知识做一个铺垫。 直方图反向投影&#xff08;Back Projection&#xff09; 还记得之前学习过的图像直方图的计算/…

Knowledge Distillation of Large Language Models

这是大模型系列模型的文章&#xff0c;针对《Knowledge Distillation of Large Language Models》的翻译。 大模型的知识蒸馏 摘要1 引言2 方法2.1 MiniLLM&#xff1a;利用逆向KLD进行知识蒸馏2.2 策略梯度优化2.3 训练算法 3 实验3.1 实验设置3.2 结果3.3 分析3.4 消融实验 …

源码编译安装LAMP——LAMP+DISCUZ论坛搭建

文章目录 一.Apache1.什么是apache2.apache的主要特点3.apache的软件版本4.编译安装优点5.安装服务的一般步骤 二.LAMP简介与概述1.LAMP平台概述2.构建LAMP平台顺序3.各组件的主要作用 三.LAMPDISCUZ论坛搭建1.部署Apache1.1 下载apache相关安装包放入服务器中1.2 解压apache的…

数学公式识别(Mathpix + pix2tex)

文章目录 数学公式识别&#xff08;Mathpix pix2tex&#xff09;1. 下载安装2. 如何使用3. 限制4. 替代品4.1 pix2tex - LaTeX OCR4.2 pix2tex&#xff08;网页版&#xff09;4.3 Simple Tex OCR 数学公式识别&#xff08;Mathpix pix2tex&#xff09; 当我们看论文时&#…

2023软件工程中各种图在现代企业级开发中的使用频率

概览 系统流程图 ✔ 数据流图 不常用 ER图 ✔ 状态转换图 ✔ Warnier图 不常用 IPO图 不常用 Petri网 不常用 层次方框图 不常用 层次图 a.k.a. H图 ✔ 1,层次图描绘软件的层次结构.层层次方框图描绘的是数据结构。 2,层次图的方框表示模块或子模块。层次方框图的方框表示数据结…

C++基础(12)——STL(vector和deque)

前言 本文主要介绍C中STL中的vector和deque容器 7.3&#xff1a;vector容器 7.3.1&#xff1a;vector容器基本概念、特点和构造函数 vector和数组的区别&#xff1a;数组是静态空间&#xff0c;一旦指定大小&#xff0c;之后就不能发生改变。vector可以动态扩展&#xff0c…

(秋招准备)三角化复习

用SVD分解继续宁特征点三角化 三角化又叫三角测量&#xff0c;本质是用相机的运动估计特征点的空间位置&#xff0c;发生在估计得到帧间运动之后(单目情况)。三角测量是指&#xff0c;通过在两处观察同一个点的夹角&#xff0c;确定该点的距离。由于双目本身就能得到视角不同的…