C++核心编程——类与对象基础

C++核心编程——类与对象基础

  • 类与对象
  • 封装
    • 构造函数
      • 普通构造
      • 拷贝构造
      • 初始化成员列表(补充)
    • 析构函数
    • 对象数组
    • 对象指针
      • 指向对象的指针
      • 指向对象成员的指针
      • this指针
    • 静态成员
      • 静态数据成员
      • 静态成员函数
    • 友元
      • 普通函数做友元函数
      • 友元成员函数
      • 友元类

类与对象

C++面向对象的三大特性为:继承、封装、多态
C++认为万事万物都皆为对象,任何一个对象都应当具有两个要素,即属性和方法
例如:

  • 人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跑、跳、吃饭、唱歌…
  • 车也可以作为对象,属性有轮胎、方向盘、车灯…, 行为有载人、放音乐、放空调…

具有相同性质的对象,我们可以抽象称为,人属于人类,车属于车类

封装

封装的意义

  • 将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相互独立,互不干扰
  • 将对象中的某些部分对外隐蔽,即隐蔽其内部细节,只留下少量接口,以便与外部连接,接收外部消息

类的声明与定义:

class Student
{
public:
	//定义一个成员函数 
	void display()
	{
		cout <<"num:"  << num << endl;
		cout <<"name:" << name << endl;
		cout <<"sex:"  << sex << endl;	
	}	
private:
	// 声明三个成员变量 
	int num;
	char name[20];
	char sex;
};

可以看到,声明类的方法是由声明结构体类型的、的方法发展而来的,一般把数据隐藏起来,而把成员函数作为对外界的接口。

class 类名
{
private:
	属性 / 行为
public:
	属性 / 行为
};

privatepublic外,还有protected权限,class类的默认权限为private其中三种权限数据的访问范围如下表:

权限数据访问空间
private类内可以访问 类外不可以访问,可被派生类访问(与protected 的区别)
public类内可以访问 类外可以访问
protected类内可以访问 类外不可以访问

实例化对象
实例化2个Student 类的对象stu1与stu2

Student stu1,stu2;

成员对象的引用
  对象名 . 成员名

stu1.num = 100;		
stu1.display();

Student *p	// 定义指针p
p = &stu1;	// p指向对象stu1
cout << p->num << endl;

Student &t = stu1;	// 定义Student 类的的引用t,并初始化为stu1
cout << t.num << endl;

成员函数
在类的内部对函数做声明,而在类外定义函数,这是程序设计的一种良好的习惯。这样不仅可以减少类体的长度,使类体清晰,便于阅读,而且能使类的接口和类的实现细节分离。

class Student
{
public:
	//定义一个成员函数 
	void display();
private:
		
	// 声明三个成员变量 
	int num;
	char name[20];
	char sex;	
};
void Student :: display()
{
	cout <<"num:"  << num << endl;
	cout <<"name:" << name << endl;
	cout <<"sex:"  << sex << endl;	
}
  • 成员函数的存储方式:同一类的不同对象中的数据成员的值一般是不相同的,而不同对象的函数的代码是相同的,不论调用哪一个对象的函数的代码,其实调用的都是相同内容的代码。
  • 不论成员函数在类内定义还是在类外定义成员函数的代码段的存储方式是相同的,都不占用对象的存储空间。

构造函数

普通构造

问题引入:怎么对成员变量初始化?
以下初始化是错误的:

class Student
{
private:
	string name="zhangsan1";
	int age=21;
	char sex='m';
	int score=100;
};

这种初始化方法显然是错误的,因为类并不是一个实体,而是一种抽象类型,并不占存储空间,显然无处容纳数据。
为了解决这个问题,C++提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行,其中函数名称与类名相同。
例如:

Student::Student()		//无参构造,默认
{
	name = "zhangsan";
	age = 20;
	sex = 'm';
	score = 100;
}

构造函数语法:类名(){}

构造函数特点

  • 函数无返回值也不写void
  • 函数名与类名相同
  • 在实例化对象的时候自动调用(不需要用户调用,也不能被调用)
  • 主要用于对象成员变量初始化
  • 构造函数可以有参数,发生函数重载

经验:通常在构造函数的声明时候指定默认参数,而不能只在定义函数时候指定默认参数
典例:已知长方体的长宽高,求长方体体积

#include<iostream>
using namespace std;

class Box
{
public:
	Box(int h=10, int w=10, int l=10);	//声明构造函数带默认参数 
	void getVolume();
private:
	int height;
	int width;
	int length;
}; 

int main()
{
	Box box1;			// 10 10 10 
	Box box2(15);		// 15 10 10
	Box box3(15,20);	// 15 20 10
	Box box4(15,20,30);	// 15 20 30
	box1.getVolume();
	box2.getVolume();
	box3.getVolume();
	box4.getVolume();
	return 0;	
}

//构造函数实现体 
Box::Box(int h, int w, int l)	//有参构造
{
	height = h;
	width  = w;
	length = l;
} 
void Box::getVolume()
{
	int volume = height*width*length;
	cout << "volume=" << volume << endl;
}

拷贝构造

在上述构造中主要涉及的为普通构造,普通构造有分为无参构造和有参构造,在C++中还存在一种拷贝构造,即构造函数传入的参数是一个类的引用,将传入参数类的属性拷贝到当前类。
拷贝构造的定义:

//拷贝构造函数实现
Person::Person(const Person &p)		//拷贝构造 记住这种写法就行 
{
	//将传入的人身上的所有属性,拷贝到我身上 
	name = p.name;
	age = p.age; 
	sex = p.sex;	
	cout << "Person的拷贝构造函数调用" << endl;
}

调用:

Person p2(p1);

在这之前,需要对p1对象进行实例化,并通过有参数构造初始化p1的值,其中p1对象的有参构造函数如下:

//有参构造实现 
Person::Person(string name, int age, char sex)
{
	this->name = name;
	this->age = age;
	this->sex = sex;	
	cout << "Person的有参构造函数调用" << endl;
}

不难看出,在这里Person构造函数发生重载,通过
Person p1("zhangsan",20,'m');先初始化p1,通过有参构造进行赋值,再通过进行拷贝构造,自动调用拷贝构造函数,则完整程序如下所示:

#include <iostream>
#include <string> 

using namespace std;

class Person
{
public:
	//构造函数发送函数重载 
	//有参构造 
	Person(string name, int age, char sex); 
	
	//拷贝构造函数
	Person(const Person &p);		//拷贝构造 记住这种写法就行 

	void show_info();				//显示信息 
private:
	string name;
	int age;
	char sex;
};

//有参构造实现 
Person::Person(string name, int age, char sex)
{
	this->name = name;
	this->age = age;
	this->sex = sex;	
	cout << "Person的有参构造函数调用" << endl;
}
//拷贝构造函数实现
Person::Person(const Person &p)		//拷贝构造 记住这种写法就行 
{
	//将传入的人身上的所有属性,拷贝到我身上 
	name = p.name;
	age = p.age; 
	sex = p.sex;	
	cout << "Person的拷贝构造函数调用" << endl;
}
void Person:: show_info()
{
	cout << "name=" << name;
	cout << "\tage=" << age;
	if(sex = 'm')
		cout << "\tsex=男" << endl;
	else
		cout << "\tsex=女" << endl;
}

int main()
{
	Person p1("zhangsan",20,'m');
	Person p2(p1);
	p1.show_info();
	p2.show_info();
	
	return 0;
}

程序运行结果:

Person的有参构造函数调用
Person的拷贝构造函数调用
name=zhangsan age=20 sex=男
name=zhangsan age=20 sex=男

整理/补充
1. 构造函数两种分类方式:

  • 按参数分为: 有参构造和无参构造
  • 按类型分为: 普通构造和拷贝构造

2. 构造函数调用规则

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数

初始化成员列表(补充)

在初始化成员属性时候,C++还提供了一种初始化数据的方法——初始化成员列表,其基本语法为:

类名::构造函数名([参数表][:初始化成员表]
{
	[构造函数体]
}

例:

Person(string name_arg, int age_arg, char sex_arg): name(name_arg), age(age_arg), sex(sex_arg){} 

说明:如果数据成员是数组, 则应当在构造函数的函数体中用语句对其赋值, 而不能在参数初始化表中对其初始化。

析构函数

析构函数语法:~类名(){}

析构函数的特点:

  • 析构函数,没有返回值也不写void
  • 函数名称与类名相同,在名称前加上符号 ~
  • 析构函数不可以有参数,因此不可以发生重载(与构造函数不同
  • 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

析构函数的作用
  析构函数的作用在于撤销对象占用的内存之前完成一些清理工作。
  实际上,析构函数的作用并不仅限于释放资源方面, 它还可以被用来执行"用户希望在最后一次使用对象之后所执行的任何操作", 例如输出有关的信息。

构造与析构的顺序:
  先构造的后析构,后构造的先析构

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作

对象数组

数组不仅可以由简单变量组成(例如整型数组的每一个元素都是整型变量),也可以由类对象组成(对象数组的每一个元素都是同类的对象)。

//假设已经声明了Student类,定义stu数组,有50个元素
Student stu[50];	

如何初始化对象数组:

  • 如果构造函数只有一个参数,在定义数组的时候可以直接在等号后面的花括号内提供实参。
    Student stu[3]={60,70,80};
    
  • 如果构造函数有多个参数,在花括号中分别写出构造函数名并在括号内指定实参。
    Student stu[3]={
    	Student("zhangsan",18,87),	//调用第一个元素的构造函数,提供3个实参
    	Student("lisi",19,76),	//调用第二个元素的构造函数,提供3个实参
    	Student("wangwu",18,72)		//调用第三个元素的构造函数,提供3个实参
    };
    
    其中3个参数分别代表姓名、年龄和分数。

对象指针

指向对象的指针

在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其数据成员。对象空间的起始地址就是对象的指针。可以定义一个指针变量,用来存放对象的地址,这就是指向对象的指针变量。
  设假定已定义Student类,在此基础上有以下语句:

Student *pt;	//定义pt为指向Student类的类对象指针
Student stu1;	//实例化对象stu1
pt = &stu1;		//将t1的起始地址赋给pt

定义指向类对象的指针变量的一般形式为:
  类名 * 对象指针名
可以通过对象指针访问对象和对象的成员:

*pt				//pt所指向的对象,即stu1
(*pt).age		//pt所指向的对象的age成员,即stu1.age
pt->name;		//pt所指向的对象的name成员,即stu1.name
(*pt).showInfo();	//调用pt所指向对象的showInfo函数,即stu1.showInfo()	
pt->showInfo();		//调用pt所指向对象的showInfo函数,即stu1.showInfo()	

指向对象成员的指针

对象有地址,存放对象的起始地址的指针变量就是指向对象的指针变量。对象成员也有地址,存放对象成员地址的指针变量就是指向对象成员的指针变量
1. 指向对象数据成员的指针

  1. 例如:
    int *p1		//指向对象成员的指针变量
    
  2. 定义
    数据类型名 * 指针变量名
  3. 引用
    p1 = &stu1.age;
    cout << * p1 << endl;
    

2. 指向对象成员函数的指针
定义指向对象成员函数的指针变量的方法和定义指向普通函数的指针变量方法有所不同
指针变量的类型必须与赋值号右侧函数的类型像匹配,要求在以下3方面都要匹配:

  1. 函数参数的类型和个数
  2. 函数的返回值的类型
  3. 所属的类

这里重温指向普通函数的指针变量的定义方法::

void (*p)()		//p是指向void 型函数的指针变量
p = fun();		//将fun函数的入口地址给p,p指向fun函数
(*p)();			//调用fun函数

而对于一个对象成员函数的指针变量如下所示:

  • 定义p2指向Student类中公用成员函数的指针变量

    void (Student::*p2)();	
    

    定义公用成员函数的指针变量一般形式为:
     数据类型名 (类名::* 指针变量名)(参数列表)

  • 初始化成员函数指针变量:

    p2 = Student::showInfo;
    

    一般形式为:
    指针变量名 = & 类名 :: 成员函数名

对象指针典例:

int main()
{
	Student stu1("zhangsan",22,90);
	
	int *p1 = &stu1.age;	//定义成员数据指针 
	cout << *p1 << endl;
	 
	Student *p2 = &stu1;	//定义类指针 
	p2->showInfo();
	
	void (Student::*p3)();	//定义成员函数指针 
	p3 =  &Student::showInfo;
	(stu1.*p3)();
	
	return 0;
} 

this指针

在每一个成员函数中都包含一个特殊的指针, 这个指针的名字是固定的, 称为 this。它是指向本类对象的指针, 它的值是当前被调用的成员函数所在的对象的起始地址。
在这里插入图片描述
this指针是隐式使用的,它是作为参数被传递给成员函数的,但有时也需要显示使用。

  • 当形参和成员变量同名时,可用this指针来区分
    Student::Student(string name, int age, int score)
    {
    	this->name = name;
    	this->age = age;
    	this->score = score;
    }
    

静态成员

静态数据成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据(数据共享)(类似于全局变量)
    • 必须类内声明,类外初始化

典例:用立方体类Box定义两个对象,引用不同对象中的静态数据成员

#include<iostream>

using namespace std;

class Box{
public:
	Box(int w, int l);
	int getVolume();
	static int height;	//把 height 定义为公用的静态数据成员
	
private:
	int width;
	int length;
};
int Box:: height = 10;		//静态成员类外初始化 
Box::Box(int w, int l)
{
	width = w;
	length = l;
}
//计算体积函数体
int Box::getVolume()
{
	int volume;
	volume = height * width * length;
	return volume;
}

int main()
{
	Box box1(15,20);
	Box box2(20,30);
	cout << box1.height << endl;	//通过对象box1引用静态数据成员height 
	cout << box2.height << endl;	//通过对象box2引用静态数据成员height 
	cout << Box::height << endl;	//通过类名引用静态数据成员height 
	
	//计算体积 
	cout << "volume=:" << box1.getVolume() << endl;
	return 0;
 } 

说明:
(1)上面的程序中,将height定义为公用的静态数据成员。
(2)可以看到,在类外可以通过对象名访问公用的静态数据成员,也可以通过类名引用静态数据成员。

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量(静态成员函数没有this指针)
  • 静态成员函数属于类的一部分,在类外调用公用的静态成员函数,要用类名和与域运算符“::”
    Box::getVolume();
    
    实际上也允许通过对象名调用静态成员变量,如:
    a.getVolume();
    
    但这不代表函数属于对象a,只是用a的类型而已

典例:统计学生平均成绩,使用静态成员函数。

#include<iostream>

using namespace std;

class Student{
public:
	Student(string, int, int);
	void get_total();
	static float get_average();
	
private:
	string name;
	int age;
	int score;
	static int sum;			//总分 
	static int count;		//人数 
}; 
//静态成员属性类外声明 
int Student::sum = 0;
int Student::count = 0;		//计数 

Student::Student(string name_temp, int age_temp, int score_temp)
{
	name = name_temp;
	age = age_temp;
	score = score_temp;
}

void Student::get_total()
{
	sum = sum + score;
	count++;			//定义已统计的人数 
}
float Student::get_average()
{
	return (sum/(float)count); 
}
int main()
{
	Student stu[3]={
		Student("zhangsan",18,87),
		Student("lisi",19,76),
		Student("wangwu",18,72),
	};
	int i,n;
	cout << "please input the number of students:";
	cin >> n;
	for(i=0;i<n;i++)
		stu[i].get_total();
	
	cout << "the average score of" << n << "student is:" <<  Student::get_average() << endl;
	 
	return 0;
}

说明:
(1)在类中定义两个静态数据成员sun和count,这是因由于这两个数据成员的值需要进行累加,并不属于某一个对象元素,而是各个元素共享的,可以看到他们的值在不断变化,始终不释放内存空间。
(2)get_total属于公用的成员函数,公用的成员函数可以访问对象的一般数据成员和静态数据成员
(3)get_average属于静态成员函数,静态成员函数只能访问静态数据成员。

思考:如何在get_total中访问stu的数据呢?
A:将对象stu的首地址作为get_total的参数。,即get_total的参数为类指针 Student *p

  • get_total定义:
    void get_total(Student *stu);
    void Student::get_total(Student *stu)
    {
    	sum = sum + stu->score;
    	cout << stu->score << endl;
    	count++;		//定义已统计的人数 
    }
    
  • get_total调用:
    for(i=0;i<n;i++)
    	stu[i].get_total(&stu[i]);
    

友元

如果在本类以外的其他地方定义了一个函数(这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数), 在类体中用friend对其进行声明此函数就称为本类的友元函数,友元函数可以访问这个类中的私有成员。

友元目的:让一个函数或者类访问另一个类中私有成员,该函数可以为全局函数,也可以为其他类的成员函数
友元语法friend 函数声明;

普通函数做友元函数

在上述的函数可以是全局的普通函数,例如:显示时间

#include<iostream>
using namespace std;

class Time{
public:
	Time(int h, int m, int s):hour(h),min(m),sec(s){};
	friend void dispaly(Time *t);	//将display作为类的友元函数, 使得display能够访问类的私有成员 
	
private:
	int hour;
	int min;
	int sec;
}; 

void dispaly(Time *t)
{
	cout << t->hour << ":" << t->min << ":" << t->sec << endl;
} 

int main()
{
	Time t1(10,17,34);
	dispaly(&t1);
	 
	return 0;
}

程序第7行将display作为类的友元函数, 使得全局函数display能够访问类的私有成员

友元成员函数

friend 函数不仅可以是一般函数(非成员函数), 而且可以是另一个类中的成员函数。

#include<iostream>
using namespace std;

class Date;
class Time{
public:
	Time(int h, int m, int s):hour(h),min(m),sec(s){};
	void display(Date &);
	
private:
	int hour;
	int min;
	int sec;
}; 

class Date{
public:
	Date(int y, int m, int d):year(y),month(m),day(d){};
	//友元Time类中的成员函数 display,使其访问该类中的私有成员 
	friend void Time::display(Date &date);
		
private:
	int year;
	int month;
	int day;
}; 

void Time::display(Date &date)
{
	cout <<  date.year << "/" << date.month << "/" <<  date.day << endl; 
	cout << hour << ":" << min << ":" << sec << endl;
} 

int main()
{
	Date date(2023,11,9);
	Time time(11,5,23);
	time.display(date);
	return 0;
}

注意在本程序中调用友元函数访问有关类的私有数据方法:

  • 在函数名display 的前面要加display所在的对象名(如time);
  • display成员函数的实参是 Date 类对象date, 否则就不能访问对象date中的私有数据;
  • 在Time::display函数中引用Date类私有数据时必须加上对象名, 如date.month.

程序说明:

  • 程序第四行为提前引用Date类,只包含类名,不包含类体,编译时的是Date是一个类名,类体将在稍后定义,没有此声明会报错。
  • 注意程序程在定义Time::display函数之前正式定义Date 类的,因为在Time::display 函数体中要用到 Date类的成员month, day, year;如果不事先定义Date类。程序将报错。

友元类

不仅可以将一个函数声明为一个类的“朋友”,而且可以将一个类(例如B类)声明为另一个类(例如A类)的“朋友”。这时B类就是A类的友元类。友元类B中的所有函数都是A类的友元函数, 可以访问A类中的所有成员。
声明友元类类的一般形式为:
  friend 类名;
说明:

  • 友元的关系是单向不是双向的
  • 友元的关系不能传递
  • 在实际工作中, 除非确有必要, 一般并不把整个类声明为友元类, 而只将确实有需要的成员函数声明为友元函数, 这样更安全一些。

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

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

相关文章

Fiddler抓包工具之fiddler的composer可以简单发送http协议的请求

一&#xff0c;composer的详解 右侧Composer区域&#xff0c;是测试接口的界面&#xff1a; 相关说明&#xff1a; 1.请求方式&#xff1a;点开可以勾选请求协议是get、post等 2.url地址栏&#xff1a;输入请求的url地址 3.请求头&#xff1a;第三块区域可以输入请求头信息…

Java实战案例————ATM

需求分析 首先ATM银行系统包括两个基础大功能&#xff1a;开户和登陆账户&#xff08;当然在系统中没有一个账户时不能登录&#xff0c;需要先开户&#xff09;。 一名用户有6项基本信息描述&#xff1a;姓名、性别、银行卡号、银行卡密码、账户余额、取款限额。 在登录账户…

MyBatis的创建,简单易懂的一篇blog

文章目录 一、MyBatis是什么二、操作流程三.配置resource总结 一、MyBatis是什么 MyBatis 是⼀款优秀的持久层框架&#xff0c;它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注…

Docker常见命令介绍

命令说明 docker pull 拉取镜像 docker push 推送镜像到DockerRegistry docker images 查看本地镜像 docker rmi 删除本地镜像 docker run 创建并运行容器&#xff08;不能重复创建&#xff09; docker stop 停止指定容器 docker start 启动指定容器 docker rest…

SpringBoot2.x整合WebService实现远程接口调用

一、添加依赖 <!-- SpringBoot 2.4 以下版本--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web-services</artifactId> </dependency><dependency><groupId>org.apach…

『测试基础』| 如何理解测试用例管理和缺陷管理?

『测试管理攻略』| 如何理解测试用例管理和缺陷管理&#xff1f; 1 测试用例定义2 测试用例设计原则3 测试用例的评审4 测试如何维护&#xff1f;5 用例的作用6 用例管理工具7 缺陷关注的重点8 缺陷分析9 缺陷管理工具 1 测试用例定义 测试用例&#xff08;TestCase&#xff0…

二叉树OJ题目——C语言

LeetCode 104.二叉树的最大深度 1. 题目描述&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例…

Docker数据卷

创建数据卷html、conf &#xff0c;分别与Nginx容器内部的html、conf目录关联。数据卷html、conf分别指向宿主机 /var/lib/docker/volumes/html/_data 目录和 /var/lib/docker/volumes/conf/_data 目录&#xff0c;将容器中的html、conf目录与宿主机的html、conf目录关联起来&a…

Leetcode—1657.确定两个字符串是否接近【中等】

2023每日刷题&#xff08;四十五&#xff09; Leetcode—1657.确定两个字符串是否接近 算法思想 源于灵神 实现代码 class Solution { public:bool closeStrings(string word1, string word2) {int len1 word1.size();int len2 word2.size();if(len1 ! len2) {return fa…

【代码】基于储能电站服务的冷热电多微网系统双层优化配置(完美复现)matlab/yalmip

程序名称&#xff1a;基于储能电站服务的冷热电多微网系统双层优化配置 实现平台&#xff1a;matlab-yalmip-cplex/gurobi 代码简介&#xff1a;代码主要做的是一个共享储能电站的双层优化配置模型&#xff0c;将储能电站服务应用到多维网系统中&#xff0c;建立了考虑不同时…

23.Python 图形化界面编程

目录 1.认识GUI和使用tkinter2.使用组件2.1 标签2.2 按钮2.3 文本框2.4 单选按钮和复选按钮2.5 菜单和消息2.6 列表框2.7 滚动条2.8 框架2.9 画布 3. 组件布局4.事件处理 1.认识GUI和使用tkinter 人机交互是从人努力适应计算机&#xff0c;到计算机不断适应人的发展过程&#…

Linux操作系统 2.Linux基础命令

Linux基础命令&#xff0c;掌握这一套清晰的讲解就够啦&#xff01; 本篇博客给大家带来的是Linux的基础命令和vi编辑器 感谢大家收看~ 一、Linux目录结构 Linux的目录结构是一个树形结构&#xff0c;Linux只有一个根目录 / 所有文件都存在/下面 Linux路径的描述方式 Linux系…

12.2_黑马Redis实战篇附近商铺用户签到UV统计

实战篇11 实战篇12 要先用test的方式把商铺的数据导入到idea当中&#xff0c;才可以进行查询噢。 代码&#xff1a; 实战篇13 thinking&#xff1a;插件mavenhelper&#xff1f; 方便处理pom文件。 实战篇15 实战篇16 thinking&#xff1a;XX.format(DateTimeFormatter.ofP…

6.8 Windows驱动开发:内核枚举Registry注册表回调

在笔者上一篇文章《内核枚举LoadImage映像回调》中LyShark教大家实现了枚举系统回调中的LoadImage通知消息&#xff0c;本章将实现对Registry注册表通知消息的枚举&#xff0c;与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NO…

23种设计模式之C++实践(一)

23种设计模式之C++实践 1. 简介2. 基础知识3. 设计模式(一)创建型模式1. 单例模式——确保对象的唯一性1.2 饿汉式单例模式1.3 懒汉式单例模式比较IoDH单例模式总结2. 简单工厂模式——集中式工厂的实现简单工厂模式总结3. 工厂方法模式——多态工厂的实现工厂方法模式总结4.…

TOP-K问题和向上调整算法和向下调整算法的时间复杂度问题的分析

TOP-K问题 TOP-K问题&#xff1a;即求数据结合中前K个最大的元素或者最小的元素&#xff0c;一般情况下数据量都比较大 比如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等 对于Top-K问题&#xff0c;能想到的最简单直接的方式就是排序&#xff0c;但是…

LD_PRELOAD劫持、ngixn临时文件、无需临时文件rce

LD_PRELOAD劫持 <1> LD_PRELOAD简介 LD_PRELOAD 是linux下的一个环境变量。用于动态链接库的加载&#xff0c;在动态链接库的过程中他的优先级是最高的。类似于 .user.ini 中的 auto_prepend_file&#xff0c;那么我们就可以在自己定义的动态链接库中装入恶意函数。 也…

maven下载和安装

maven下载和安装 一、概述 Maven是一个项目管理工具&#xff0c;它包含了一个项目对象模型 (Project Object Model)&#xff0c;一组标准集合&#xff0c;一个项目生命周期(Project Lifecycle)&#xff0c;一个依赖管理系统(Dependency Management System)&#xff0c;和用来…

算法通关村第十四关-白银挑战堆的经典问题

大家好我是苏麟 , 今天带来堆的一些经典问题 , 我们一起研究一下 . 大纲 数组中的第K个最大元素合并 K 个升序链表 数组中的第K个最大元素 描述 : 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k …

Hdoop学习笔记(HDP)-Part.14 安装YARN+MR

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …