C++的继承

目录

前言

继承的概念和定义 

访问权限表

基类和派生类对象的赋值转换

继承中的作用域

派生类的默认成员函数

继承与友元

继承与静态成员

复杂的菱形继承和菱形虚拟继承

菱形虚拟继承

观察内存 

注意事项:对象在内存中的存储顺序是按声明的顺序存储的 

继承(is-a)和组合(has-a)


前言

        STL中不同的“容器”的迭代器在使用时是一样的,但实际上迭代器的底层实现并不相同,对于vector、string等空间连续的“容器”,它们的迭代器可以直接使用原生指针,而对于list、stack和quque等空间不连续的“容器”,它们的迭代器需要对原生指针进行封装后才能使用

在STL中,“容器”指代各种数据结构,容器的概念是在docker中才出现的

封装:

  1. 数据和方法放在一起,将可以被外部访问的定义成共有,不想访问的定义成私有和保护
  2. 一个类型放到另一个类型内,通过“typedef”或 “成员函数的调整”封装出另一个全新的类型

继承的概念和定义 

基本概念:允许一个类(称为子类或派生类)获取另一个类(称为父类或基类)的属性和方法。通过继承,子类可以重用父类的代码,并且可以在此基础上添加新的属性和方法,或者修改现有行为

格式:class 派生类(子类)  : 继承方式 基类(父类)

  • 继承方式:public(共有继承)、protect(保护继承)、private(私有继承)
  • 访问限定符:public(共有访问)、protect(保护访问)、private(私有访问)
#include <iostream>
#include <string >
using namespace std;

//基类Person
class Person
{
public://共有
	void Print()
	{
		cout << "name: " << _name << endl;
		cout << "age: " << _age << endl;
	}

private://私有
	int _age = 18;
	string _name = "Mike";
};

//派生类Student 
class Student : public Person
{
protected:
	int _stuid;//学号
};

//派生类Teacher 
class Teacher : public Person
{
protected:
	int _jobid;//工号
};

int main()
{
	Student std;
	Teacher tea;
	tea.Print();//成功调用基类中的Print函数
	std.Print();//成功调用基类中的Print函数
	return 0;
}

注意事项:继承后基类的的成员都会变成子类的一部分

访问权限表

访问限定 / 继承方式公有继承保护继承私有继承
基类的公有成员

是派生类的公有成员(重点)

是派生类的保护成员(重点)

是派生类的 私有 成员
基类的保护成员
是派生类的 保护 成员 (重点)
是派生类的 保护
成员 (重点)
是派生类的 私有 成员
  基类的私有成员 
派生类中不可见
派生类中不可见
同左

 结论:

1、基类的private成员在派生类中不可见是指,虽然该private成员被继承到了派生类对象中,但是派生类在类内和类外都不能直接访问基类的private成员,但是可以通过调用基类中的成员函数间接访问private成员(在派生类中通过调用基类的Print函数间接访问不能直接访问的_age) 

2、 基类的其它成员在子类的访问方式 = Min(成员在基类的访问限定符,派生类的继承方式),public > protected > private(_name在基类中的访问限定是保护,派生类Student共有继承,则_name在Student中的访问权限是保护,在类外不能直接访问,Print函数在基类中的访问限定共有,共有碰共有,则Print函数在Student中的访问权限也是共有,在类外可以直接访问)

3、若想要基类的成员不被类外直接访问,就将该成员的访问限定设置为为private

4、若想要基类的成员不被类外直接访问,但需要让其派生类可以访问,就将该成员的访问限定设置为protect(保护成员限定符是因为继承才出现的)

4、struct默认继承方式和访问限定符都是公有, class 默认继承方式和访问限定符都是私有

struct Student (默认为public): Person;
class Student (默认为private): Person;

struct Student 
{
    //public
}

class Student 
{
    //private
}

5、在实际应用中一般都是public继承,很少使用protected / private继承

基类和派生类对象的赋值转换

基本概念:派生类以公有的方式继承基类,则可以将该派生类视为一个特殊的基类(public继承,每个子类对象都是一个特殊的父类对象)

切片/割:子类对象赋值给父类对象的过程

注意事项:

1、只能子赋值给父,不能父赋值给子

2、内数置类型直接赋值,自定义类型会调用该自定义类型的拷贝构造函 

3、切片具有赋值兼容,在赋值过程中不会产生临时变量

4、派生类赋值给引用 或 指针,则引用或指针指向的就是派生类中的基类部分的内容,且可以通过引用和指针修改这一部分内容

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

//基类Person
class Person
{
public://共有
	void Print()
	{
		cout << "name: " << _name << endl;
		cout << "age: " << _age << endl;
	}

	string _name = "Mike";
protected://保护
	
private://私有

	int _age = 18;

};

//派生类Student 
class Student : public Person
{
public:
	void func()
	{
		cout << _name << endl;
		//cout << _age << endl;//派生类不能直接访问基类的私有成员
		Print();//但是可以通过调用基类的成员函数间接访问
	}
protected:
	int _stuid;//学号
};

//派生类Teacher 
class Teacher : public Person
{
protected:
	int _jobid;//工号
};

int main()
{
	//基类和派生类对象的赋值转换
	Student st;
	Person p = st;
	Person& ref = st;
	Person* ptr = &st;

	ref._name += 'x';
	ptr->_name += 'y';

	return 0;
}

5、在特殊情况下,指针和引用也可以将子类自己的内容赋值给父类 

继承中的作用域

目前已知的四个域:局部域、全局域、命名空间域、类域

局部域和全局域影响生命周期,命名空间域和类域不影响生命周期

同一域中,不构成重载的函数不能重名,变量不能存在重名

基本概念:

1、基类和派生类都有独立的作用域(二者是独立的作用域)

 2、子类和父类中有同名成员,子类成员会屏蔽对父类的同名成员的直接访问,这种情况叫隐藏也叫重定义,如果在子类中真的想要访问父类的同名成员,则需要使用域作用限定符::显示访问(基类::重名的基类成员)

3、父类和子类中的成员函数只要重名就会隐藏,不会构成重载

4、在继承体系中最好不要定义同名成员

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

class A
{
public:
	void fun()
	{
		cout << "父类fun()" << endl;
	}
};


class B : public A
{
public:
	void fun(int i)
	{
		A::fun();
		cout << "子类fun()" << endl;
	}
};

int main()
{
	//继承中的作用域
	B bb;
	bb.fun(1);
	//bb.fun();//尝试访问父类的fun函数失败
	bb.A::fun();
	return 0;
}

派生类的默认成员函数

构造函数:必须调用基类的构造函数初始化基类的那一部分的成员,如果基类没有默认构造就显示调用基类的构造函数(默认构造和构造函数不一样,注意二者区别),然后处理派生类独有的成员

  • 隐式调用:
#include <iostream>
#include <vector>
using namespace std;

class Base {
public:
    Base() 
    {
        cout << "Base constructor called." << endl;
    }
};

class Derived : public Base {
public:
    Derived() 
    { //隐式调用基类的默认构造函数:编译器自动调用了,通过调试可以发现
        cout << "Derived constructor called." << endl;
    }
};

int main() {
    Derived d;  // 创建派生对象
}
  • 显示调用:
#include <iostream>
using namespace std;

class Base {
public:
    Base(int x) 
    {
        cout << "Base constructor called with x = " << x << endl;
    }
};

class Derived : public Base {
public:
    Derived(int y)
     :Base(10) //显式调用基类的构造函数(而不是默认构造函数)
    { 
        cout << "Derived constructor called with y = " << y << endl;
    }
};

int main() {
    Derived d(20);  // 创建派生对象
}

拷贝构造函数:必须调用基类的拷贝构造完成对基类那一部分的成员的拷贝,然后处理派生类独有的成员

赋值重载函数:必须调用基类的赋值运算符重载函数,完成对基类成员的复制,然后处理派生类独有的成员

析构函数:派生类的析构函数会在被调用玩抽自动调用基类的析构函数从而清理基类成员,只有这样才能保证析构时是先子后父(子类析构中可能还会用到父类的一些内容,父类先析构了子类就访问不到了) 

注意事项:因为多态,析构函数名字会被统一处理成destructor,所以子类的析构也会隐藏父类,

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

class Person
{
public:
	Person(const char* name)//初始化队列
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)//拷贝构造,(this.s)
		: _name(p._name)//this->_name(s._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)//赋值重载
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}

	~Person()//析构函数
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};

class Student : public Person
{
public:
	//父类 + 子类
	Student(int num, const char* str, const char* name)//初始化队列
		:Person(name)//父类的就调用父类的初始化列表
		, _num(num)//子类
		, _str(str)//子类
	{
		cout << "Student()" << endl;
	}

	// s2(s1)
	Student(const Student& s)//拷贝构造,(const Student& this ,const Student& s)
		:Person(s)//传引用切片,初始化一个父类的内容,调用父类的拷贝构造,Person(this,s)
		, _num(s._num)//然后再将子类中特有的进行初始化
		, _str(s._str)
	{}

	Student& operator=(const Student& s)//赋值重载
	{
		if (this != &s)//不能自己给自己赋值
		{
			Person::operator=(s);//调用父类的赋值重载,如果不指定父类的话子类会将父类的赋值重载隐藏,无限循环调用子类自己的赋值重载最后栈溢出
			_num = s._num;
			_str = s._str;
		}

		return *this;
	}

	// 子类的析构也会隐藏父类
	// 因为后续多态的需要,析构函数名字会被统一处理成destructor
	~Student()//析构函数
	{
		cout << _name << endl;
		cout << "~Student()" << endl;
	}

protected:
	int _num;	 
	string _str;

};

int main()
{
	
	Student s1(1, "xxxx", "张三");//初始化
	//Student s2(s1);//拷贝构造

	//Student s3(2, "yyy", "李四");//初始化
	//s1 = s3;//赋值重载

	//Person p("李四");//初始化父类
	//p.~Person();//析构函数可以直接显示调用

	return 0;
}

继承与友元

基本概念:父类的友元关系子类不能继承,若派生类也想要和父类的友元函数成为自己的友元函数则需要再次声明(你父亲的朋友不是你的朋友)

//正确示例:
#include <iostream>
#include <string>
using namespace std;

class Student;//前向声明,person的友元函数需要一个Student类类型的对象,如果不事先声明则该友元函数因为没有可匹配的对象会被忽略
class Person
{
public:
	friend void Display(const Person& p, const Student& s);//可以访问_name
protected:
	string _name;
};

class Student : public Person
{
public:
	friend void Display(const Person& p, const Student& s);//可以访问//_stuNum
protected:
	int _stuNum;
};

void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

int main()
{
	Person p;
	Student s;
	Display(p,s);
	return 0;
}

 注意事项:

1、某类的友元函数可以访问该类中被保护的或是私有的成员 

2、有时需要前向声明,否则会报错

继承与静态成员

基本概念:静态成员变量属于当前类也属于当前类的所有派生类(一家子公用的)

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

class Person
{
public:
	Person()
	{
		++_count;
	}
protected:
	string _name;//姓名
public:
	static int _count; //统计人的个数(静态成员)
};

int Person::_count = 0;

class Student :public Person
{
protected:
	int _stuNum;//学号
};

class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究项目
};

int main()
{
	Person p;
	Student s;
	cout << &Person::_count << endl;
	cout << &Student::_count << endl;
	cout << &Graduate::_count << endl;
	return 0;
}

注意事项:派生类在实例化时都会调用父类的相关构造(孙子也会调用爷爷的)

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

class Person
{
public:
	Person()//默认构造
	{
		++_count;//调用默认构造++_count
	}
	Person(const Person& p)//拷贝构造
	{
		++_count;//调用拷贝构造++_count
	}
	
protected:
	string _name;//姓名
public:
	static int _count; //统计人的个数(静态成员)
};

int Person::_count = 0;

class Student :public Person
{
protected:
	int _stuNum;//学号
};

class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究项目
};

int main()
{
	Person p;
	Student s1;
	Student s2;
	Student s3;
	Student s4(s3);
	Graduate g1;


	cout << &Person::_count << endl;
	cout << &Student::_count << endl;
	cout << &Graduate::_count << endl;

	cout << Person::_count << endl;
	cout << Student::_count << endl;
	cout << Graduate::_count << endl;
	return 0;
}

复杂的菱形继承和菱形虚拟继承

单继承:一个子类只有一个直接父类

多继承:一个子类有两个或以上直接父类(番茄即是水果又是蔬菜)

菱形继承:多继承的一种特殊情况(助教即是老师也是学生)

virtual修饰的是在第一次分叉处的类

菱形继承的问题: 数据冗余和二义性问题

数据冗余:Assistant的对象中会有两份Person的成员,并且修改student中的person中成员name时,teacher中的相关name也会被修改,但是一名助教在作为学生时的名字是小赵,但是作为老师时的名字是张助教,即student和teacher中存的_name应该不同,但菱形继承导致助教只能用一个称呼

二义性:访问Assistan类类型的对象中的_name(Person中的)时,编译器不知道要访问哪个父类中的成员_name(当然可以通过域作用限定符显示访问,但是数据冗余问题得不到解决)

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

class Person
{
public:
	string _name; // 姓名
};

class Student : public Person
{
protected:
	int _num; //学号
};

class Teacher : public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

菱形虚拟继承

基本概念:在菱形继承的腰部位置,加上virtual关键字

功能:解决数据冗余和二义性问题

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

class Person
{
public:
	string _name; // 姓名
};

class Student : virtual public Person
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

观察内存 

#include <iostream>
using namespace std;

class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A
{
public:
	int _b;
};
// class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

        virual修饰时,_a被放在了最下方的位置,同时在B和C类中有一个存放某地址的指针,由于是小端,低地址存低字节,两个地址分别是:0x0038AE1C和0x0038AE24,在内存窗口取地址发现它们应该分别指向两块空间,即虚基表,而那两个指针叫做虚基指针

        第一个虚基表中存放的是16进制的14,对应的十进制是20,第二个虚基表中存放的是16进制的0c,对应的十进制是12,它们是虚基表中存放的偏移量,虚基指针可以通过这些偏移量找到虚基A(0X005EF75C + 14 = 0X005EF770、0X005EF764 + 0C = 0X005EF770)

//派生类赋值给基类时会发生切片
D d;
B b = d; 
C c = d; 

//两个将基类的指针存放派生类的地址时,会发生指针偏移,指向自己的对象位置
B* pb = &d;
C* pc = &d;
pb->_a++;
pc->_a++;

B、C类虚继承A类,_a 直接被放到了一个公共地址,如果 B 要找 _a 就需要虚基表 


注意事项:对象在内存中的存储顺序是按声明的顺序存储的 

结论:尽量不设计菱形继承 

继承(is-a)和组合(has-a)

//组合
class A
{
private:
	int _a;
};

class B
{
private:
	A __aa;
	int _b;
};
//继承
class A
{
private:
	int _a;
};

class B : public A
{
private:
	int _b;
};

 注意事项:

1、默认情况下public继承,是一种is-a关系,即每个派生类对象都是一个基类对象(猴子是一种动物,猴子是派生类,动物是基类)

2、组合是一种has-a关系,B组合了A,则每个B类对象中都有一个A类对象(动物园拥有猴子)

3、优先使用对象组合,而不是类继承

4、组合和继承都是一种复用

5、继承允许根据基类的实现来定义派生类的实现,这种通过生成派生类的复用通常称为白箱复用,因为基类的内部细节对子类可见,派生类和基类间的依赖关系很强,耦合度高

6、对象组合是类继承之外的另一种复用选择,可以将更复杂的功能通过组装或组合对象得到 。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复 用,因为对象的内部细节是不可见的, 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被 封装

7、软件设计应该是低耦合高内聚

  • 低耦合:类和类之间的关系、模块和模块之间的关系,不那么紧密,关联度不高
  • 高内聚:当前类或模块中各种方法的关联程度高

8、适合is-a关系就用继承,适合has-a关系就用组合,都适合就用组合

~over~

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

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

相关文章

Python分析之3 种空间插值方法

插值是一个非常常见的数学概念,不仅数据科学家使用它,而且各个领域的人们也使用它。然而,在处理地理空间数据时,插值变得更加复杂,因为您需要基于几个通常稀疏的观测值创建代表性网格。 在深入研究地理空间部分之前,让我们简要回顾一下线性插值。 为了演示的目的,我将使…

ansible模块实战-部署rsync服务端

目录 1、根据部署流程所用到的命令找出模块 2.实战部署 2.1 服务部署&#xff1a;yum 安装 2.2 准备好rsync服务的配置文件 &#xff0c;并将配置文件通过copy模块分发给192.168.81.136这台受控主机 2.3 创建虚拟机用户 2.4 创建密码文件和改权限 2.5 模块对应目录&…

2024百度网盘超级会员怎么购买才能最省钱且不会踩坑?我来给你分享一下

不知道百度网盘超级会员怎么买最便宜&#xff0c;我来告诉你通过百度网盘最新优惠活动最低仅需188元/年。下面就和大家详细分享百度网盘会员最便宜的购买方法&#xff0c;值得你收藏和点赞&#xff01;当我们需要选择网盘时&#xff0c;大部分同学都会选择百度网盘&#xff0c;…

2024年五一杯数学建模B题思路分析

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

python聊天室

python聊天室 文章目录 python聊天室chat_serverchat_client使用方式1.局域网聊天2.公网聊天 下面是一个简单的示例&#xff0c;包含了chat_client.py和chat_server.py的代码。 chat_server chat_server.py监听指定的端口&#xff0c;并接收来自客户端的消息&#xff0c;并将消…

蓝桥杯2024年第十五届省赛真题-好数

思路&#xff1a;枚举所有数&#xff0c;每个数分别判断。代码时间复杂度虽然是n^2&#xff0c;但是由于判断的数长度最长是7位&#xff0c;用字符串处理最多只循环7次&#xff0c;所以最大时间复杂度小 7*10的七次方&#xff0c;不会超时。库中的to_string时间复杂度太大&…

自己的事情自己做:使用 Python Turtle 绘制 Python Logo

以下代码中&#xff0c;将向你展示一个有趣的程序&#xff0c;如何使用 Python Turtle 中绘制 Python Logo。Python 翻译成汉语是蟒蛇的意思&#xff0c;Python 的 Logo 也是两条缠绕在一起的蟒蛇。 import turtlepen turtle.Turtle() turtle.bgcolor("black") pe…

元宇宙VR虚拟线上展馆满足企业快速布展的需要

想要拥有一个VR线上虚拟展馆&#xff0c;展现您的城市风采或企业特色吗? 相比实体展馆搭建&#xff0c;VR线上虚拟展馆投入资金少&#xff0c;回报周期短&#xff0c;只需几个月的时间&#xff0c;您就能开始资金回笼。那么一个VR线上虚拟展馆多少钱呢? 深圳VR公司华锐视点基…

LeetCode701:二叉搜索树中的插入操作

题目描述 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 &#xff0c;新值和原始二叉搜索树中的任意节点值都不同。 代码 递归法 class Solution { public…

5GNR刷题

5G帧结构 5G NR帧结构的基本时间单位是( C ) A) subframe B) slot C) Tc D) symbol 5G无线帧长是多少ms&#xff08;B&#xff09; A) 5 B) 10 C) 20 D) 40 下面哪种子载波间隔是中国移动白皮书中规定必选(B ) A) 15KHz B) 30KHz C) 60KHz D) 120KHz 5G参数集包含哪…

【学习笔记二十】EWM TU运输单元业务概述及后台配置

一、EWM TU运输单元业务流程概述 TU是指车辆和运输单元在货场中,移动车辆或运输单元。 车辆是特定运输方式的专用化工具,车辆可以包含一个或多个运输单元,并代表运输车辆的实际实体。 运输单元是用于运输货物的车辆的最小可装载单位,运输单元可以是车辆的固定部分。 …

游游的you矩阵

题目&#xff1a; 游游拿到了一个字符矩阵&#xff0c;她想知道有多少个三角形满足以下条件&#xff1a; 三角形的三个顶点分别是 y、o、u 字符。三角形为直角三角形&#xff0c;且两个直角边一个为水平、另一个为垂直。 输入描述&#xff1a; 第一行输入两个正整数n,m&#…

数字营销:细分-目标-定位(STP)模式——如何实现精准营销

细分-目标-定位&#xff08;STP&#xff09;模型是最广为人知的营销策略之一。作为营销人员&#xff0c;我们倾向于追逐新鲜事物&#xff0c;总是追求最新、最闪亮的营销技术&#xff0c;并为自己领先于趋势而感到自豪。与内容营销相结合&#xff0c;STP模式仍然是简化营销运作…

【详细的Kylin使用心得】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

【JavaWeb】异步请求——AJAX

目录 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;优点传统Web与Ajax的差异Ajax工作流程Ajax 经典应用场景XMLHttpRequest常用方法事件常用属性 ajax: GET请求和POST请求的区别 传统Ajax实现传统方式实现Ajax的不足 $.ajax()语法常用属性参数常用函数参数 Aja…

【LeetCode题解】2007. 从双倍数组中还原原数组

文章目录 [2007. 从双倍数组中还原原数组](https://leetcode.cn/problems/find-original-array-from-doubled-array/)思路&#xff1a;代码&#xff1a; 2007. 从双倍数组中还原原数组 思路&#xff1a; 首先&#xff0c;对输入的 changed 数组进行排序&#xff0c;以便后续操…

隐式/动态游标的创建与使用

目录 将 emp 数据表中部门 10 的员工工资增加 100 元&#xff0c;然后使用隐式游标的 %ROWCOUNT 属性输出涉及的员工数量 动态游标的定义 声明游标变量 打开游标变量 检索游标变量 关闭游标变量 定义动态游标&#xff0c;输出 emp 中部门 10 的所有员工的工号和姓名 Orac…

LeetCode-热题100:102. 二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a; root [3,9,20,null,null,15,7] 输出&#xff1a; [[3],[9,20],[15,7]] 示例 2&am…

数据结构之排序了如指掌(二)

目录 题外话 正题 选择排序 选择排序思路 选择排序代码详解 选择排序复杂度 双向选择排序 双向选择排序思路 双向选择排序代码详解 堆排序 堆排序思路 堆排序代码详解 堆排序复杂度 冒泡排序 冒泡排序思路 冒泡排序代码详解 冒泡排序复杂度 小结 题外话 今天…

供应链投毒预警 | 开源供应链投毒202403月报发布啦!(含投毒案例分析)

悬镜供应链安全情报中心通过持续监测全网主流开源软件仓库&#xff0c;结合程序动静态分析方式对潜在风险的开源组件包进行动态跟踪和捕获&#xff0c;能够第一时间捕获开源组件仓库中的恶意投毒攻击。在2024年3月份&#xff0c;悬镜供应链安全情报中心在NPM官方仓库&#xff0…