C++面向对象编程

C++面向对象编程

面向对象基础

实体(属性,行为) ADT(abstract data type)

面向对象语言的四大特征:抽象,封装(隐藏),继承,多态

访问限定符:public 共有的,private 私有的, protected 保护的

类的成员函数和内联函数inline的区别?

一个类的成员方法,在类内声明和定义,那么它本身就是inline函数。如果类的成员方法在类内声明,类外定义,且没有关键字inline修饰,那么该方法就是类的普通的函数。如果在类外定义类的成员方法,并且用inline关键字修饰为内联函数,则该函数必须写在和声名在一个头文件中,,否则编译就会出现问题。具体查看inline 成员函数。

有无inline修饰的区别在于:普通函数调用需要先开辟形参变量内存空间,在运行函数主体,结束时再回收空间。而inline修饰后可以类似于将函数体直接转在调用点后面,就节省了函数调用内存空间的开辟和回收。但是inline是否起作用,还需要看编译器是否将其编译为内敛函数,如果太过于复杂的函数体,那么极大可能就不会成为内联函数。具体可以查看C++函数调用那些事。

C++对象内存大小的计算方法:

对象的内存大小只和成员变量有关,和成员方法无关。

可以使用vs中的终端,使用命令cl xxx.cpp /d1eportSingleClassLayoutXXX查看xxx.cppXXX类的内存大小。

#include<iostream>
#include<string>

using namespace std;

const int LEN = 20;
class TestObj
{
public:
	void setX(int n);
	void setY(int n);
	void setName(const char* name);
private:
	int x;
	int y;
	char name[LEN];
};
int main()
{

	return 0;
}


C++类对象属性是独立的,也就是各个对象的成员属性是单独的,互不影响。但是类的成员方法是公用的,类对象都可以访问,编译后都放在代码段。

类的成员方法一经过编译,所有的方法参数,都会加一个this指针,接收调用该方法的对象的地址。

类的构造函数和析构函数

没有提供任何构造函数的时候,编译器会自动生成默认构造和默认析构,是空函数(函数里面没有函数体部分)。自己定义了构造函数后,编译器就不会在创建默认构造函数。

对象析构的顺序和创建的顺序相反:后构造的先析构

//构造函数和析构函数,实现一个顺序栈
//构造函数和析构函数和类名一样,没有返回值

#include<iostream>

using namespace std;

//构造函数和析构函数,实现一个顺序栈
//构造函数和析构函数和类名一样,没有返回值
class SeqStack
{
public:
	void init(int size = 10)
	{
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}
	SeqStack(int size=10) //是可以带参数的,因此可以提供多个构造函数
	{	
		cout << this << "SeqStack" << endl;
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}
	~SeqStack() //没有参数的,所以析构函数只有一个,析构函数调用以后,对象就不存在了
	{
		cout << this << "~SeqStack" << endl;
		delete[] _pstack;
		_pstack = nullptr;
	}
	void release()
	{
		delete[] _pstack;
		_pstack = nullptr;
	}
	void push(int val)
	{
		if (full()) resize();
		_pstack[++_top] = val;
	}
	void pop()
	{
		if (empty()) return;
		--_top;
	}
	int top()
	{
		return _pstack[_top];
	}
	bool empty() { return _top == -1; }
	bool full() { return _top == _size - 1; }

private:
	int* _pstack;//动态开辟数组,存储顺序栈的元素
	int _top;//顶部元素的值
	int _size;//数组扩容的总大小

	void resize()
	{
		int* ptmp = new int[_size * 2];
		for (int i = 0; i < _size; i++)
		{
			ptmp[i] = _pstack[i];
		}
		delete[] _pstack;
		_pstack = ptmp;
		_size = _size * 2;
	}
};
int main()
{
	SeqStack s;
	//s.init(5); //空间初始化,现在用构造函数代替,初始化操作容易被忘记
	for (int i = 0; i < 15; i++)
	{
		s.push(i);
	}
	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;
	//s.release();//堆空间析构,现在用析构函数代替,因为空间释放很容易被忘记

	SeqStack s2(50);
	
	//s2.~SeqStack();//自己调用析构函数释放资源,一般不自己写调用析构函数
	
	SeqStack s3=new SeqStack();//malloc内存开辟,在使用构造函数进行对象初始化
	//必须显式回收s3的内存资源,否则作用域结束后也不会析构,因为new创建的对象存储在堆区
	delete s3;//先执行析构s3.~SeqStack();在执行free(ps);

	return 0;
}

对象的深拷贝和浅拷贝

对象的默认拷贝构造函数是做内存的数据拷贝,关键是对象如果占用外部资源(使用堆空间),那么浅拷贝就会出现问题。

所以在类创建的对象存在外部资源访问的情况下,需要重新定义类的默认拷贝构造函数和operator=函数,否则可能会出现对象析构时出现释放野指针的情况。

还是以SeqStack为例,主要看拷贝构造函数和operator=函数,以及main函数的注释代码。

#include<iostream>

using namespace std;

//构造函数和析构函数,实现一个顺序栈
//构造函数和析构函数和类名一样,没有返回值
class SeqStack
{
public:
	void init(int size = 10)
	{
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}
	SeqStack(int size=10)
	{	
		cout << this << "SeqStack" << endl;
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}
	SeqStack(const SeqStack& src)
	{
		/*
		//默认的拷贝构造函数长这样
		_pstack = src._pstack;//指针赋值,也就是将新的对象的_pstack也指向原来的src._pstack的地址
		_top = src._top;
		_size = src._size;
		*/
		cout << "SeqStack(const SeqStack&)" << endl;
		_pstack = new int[src._size];
		for (int i = 0; i < src._top; i++)//还需要将内存中的值及进行拷贝
		{
			_pstack[i] = src._pstack[i];
		}
		_top = src._top;
		_size = src._size;
	}
	void operator=(const SeqStack& src)
	{
		cout << "operator=" << endl;
		//需要先释放当前对象的内存,防止内存丢失(因为采用=重载,左边的对象一般存在空间)
		//防止自己给自己赋值
		if (this == &src)
		{
			return;
		}
		delete[]_pstack;

		_pstack = new int[src._size];
		for (int i = 0; i < src._top; i++)//还需要将内存中的值及进行拷贝
		{
			_pstack[i] = src._pstack[i];
		}
		_top = src._top;
		_size = src._size;
	}
	~SeqStack()
	{
		cout << this << "~SeqStack" << endl;
		delete[] _pstack;
		_pstack = nullptr;
	}
	void release()
	{
		delete[] _pstack;
		_pstack = nullptr;
	}
	void push(int val)
	{
		if (full()) resize();
		_pstack[++_top] = val;
	}
	void pop()
	{
		if (empty()) return;
		--_top;
	}
	int top()
	{
		return _pstack[_top];
	}
	bool empty() { return _top == -1; }
	bool full() { return _top == _size - 1; }

private:
	int* _pstack;//动态开辟数组,存储顺序栈的元素
	int _top;//顶部元素的值
	int _size;//数组扩容的总大小

	void resize()
	{
		int* ptmp = new int[_size * 2];
		for (int i = 0; i < _size; i++)
		{
			ptmp[i] = _pstack[i];
		}
		delete[] _pstack;
		_pstack = ptmp;
		_size = _size * 2;
	}
};
int main()
{
	SeqStack s1(10);
	/*
	调用默认拷贝构造函数进行对象s2的实例化,这里会存在一个严重的问题,因为SeqStack会开辟一个堆空间,
	调用默认拷贝构造函数会导致s2和s1共用一个堆内存空间,在s2析构完后会将空间释放,
	而s1在释放该内存就会出现内存访问异常
	*/
	//SeqStack s2 = s1;//这里使用默认拷贝构造会导致问题,具体查看拷贝构造函数
	//SeqStack s3(s1);//其实和上面SeqStack s2 = s1;一样,也是调用拷贝构造函数

	SeqStack s2 = s1;

	//s2.operator=()
	s2 = s1;//如果对象没有重写=操作符函数,那么默认就是做浅拷贝,内存数据的拷贝,类似默认拷贝构造函数
	//而且这里会存在一个内存泄漏的严重问题,将s1._pstack -> s2._pstack导致原来的s2._pstack没有释放就被丢弃了
	//这里也会出现内存访问异常,想再次释放空间的错误,释放野指针
	return 0;
}

拷贝构造函数和赋值操作符重载实践

自定义实现String

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

class String
{
public:
	String(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			m_data = new char[strlen(str) + 1];
			strcpy(m_data, str);
		}
		else
		{
			m_data = new char[1];
			m_data[0] = '\0';
		}
	}
	String(const String& other)
	{
		m_data = new char[strlen(other.m_data) + 1];
		strcpy(m_data, other.m_data);
	}
	~String()
	{
		delete[]m_data;
		m_data = nullptr;
	}
	//String& 是为了实现多个后续情况 str1=str2=str3;
	String& operator=(const String& other)
	{
		if (this == &other)
		{
			return *this;
		}
		delete[]m_data;
		m_data = new char[strlen(other.m_data) + 1];
		strcpy(m_data, other.m_data);
		return *this;
	}

private:
	char* m_data;
};
int main()
{
	//调用String(const char* str = nullptr)构造函数
	String str1;
	String str2("hello");
	String str3 = "hello";
	
	//调用拷贝构造函数
	String str4(str3);
	String str5 = str3;
	
	//赋值操作符重载
	str1 = str2;
	return 0;
}

自定义实现循环Queue

#include <iostream>
using namespace std;

class Queue
{
public:
	Queue(int size = 10)
	{
		_pQue = new int[size];
		_front = _rear = 0;
		_size = size;
	}
	Queue(const Queue& src)
	{
		_front = src._front;
		_rear = src._rear;
		_size = src._size;
		_pQue = new int[_size];
		for (int i = src._front; i != _rear;i=(i+1)%_size)
		{
			_pQue[i] = src._pQue[i];
		}
	}
	Queue& operator=(const Queue&src)
	{
		if(this==&src)
		{
			return *this;
		}
		delete[] _pQue;
		_front = src._front;
		_rear = src._rear;
		_size = src._size;
		_pQue = new int[_size];
		for (int i = src._front; i != _rear; i = (i + 1) % _size)
		{
			_pQue[i] = src._pQue[i];
		}
	}
	~Queue()
	{
		delete[] _pQue;
		_pQue = nullptr;
	}
	void push(int val)
	{
		if (full())
			resize();
		_pQue[_rear++] = val;
		_rear = _rear % _size;
	}
	void pop()
	{
		if (empty())
			return;
		_front = (_front + 1) % _size;
	}
	int front()
	{
		return _pQue[_front];
	}
	bool full()
	{
		return (_rear + 1) % _size == _front;
	}
	bool empty()
	{
		return _rear == _front;
	}

private:
	int *_pQue; // 申请队列的空间数组
	int _front; // 指示队头的位置
	int _rear;	// 指示队尾的位置
	int _size;

	void resize()
	{
		int *temp = new int[_size * 2];
		int index = 0;
		for (int i = _front; i != _rear; i = (i + 1) % _size)
		{
			temp[index++] = _pQue[i];
		}
		delete[] _pQue;
		_pQue = temp;
		_front = 0;
		_rear = index;
		_size = 2 * _size;
	}
};
int main()
{
	Queue q;
	for (int i = 0; i < 30; i++)
	{
		q.push(i);
	}
	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}

	Queue m = q;
	m = q;
	return 0;
}

构造函数的初始化列表

使用类的对象作为一个类的成员变量,这里举了一个商品和事件的示例:主要看Goods类构造函数的解析

class Goods
{
public:
	Goods(const char* n, int a, int p, int y, int m, int d)
		:_data(y, m, d) //Data _data(y,m,d); 这里会调用Data的构造函数,也称为构造函数的初始化列表 #step1先执行
		//,_amount(a) //int _amount=a;
	{
		// #step2 后执行
		strcpy(_name, n);
		_amount = a; //int _amount;_amount=a;
		_price = p;
	}
	void show()
	{
		cout << "name: " << _name << endl;
		cout << "amount: " << _amount << endl;
		cout << "price: " << _price << endl;
		_data.show();
	}
private:
	char _name[20];
	int _amount;
	double _price;
	Data _data;//如果没有默认构造函数就会初始化错误,没有构造函数可用
};
int main()
{
	Goods g("面包", 100, 25.0, 2023, 8, 1);
	g.show();

	Test t;
	t.show();
	return 0;
}

成员变量的初始化顺序和它们定义的顺序有关,和构造函数的初始化列表中出现的先后顺序无关

class Test
{
public:
	Test(int data = 10) :mb(data), ma(mb) {};//这里初始化的顺序按照定义的顺序,ma先初始化,mb后初始化
	void show() { cout << "ma: " << ma << "mb: " << mb << endl; } //ma: -858993460mb: 10
private:
	//成员变量的初始化顺序和它们定义的顺序有关,和构造函数的初始化列表中出现的先后顺序无关。
	int ma;
	int mb;
};

类的各种成员

类的各种成员方法,包括类的成员方法和类的成员变量。

普通成员变量和普通成员方法都依赖于对象,适用对象进行访问和调用(这是因为普通成员方法都会产生一个this指针)。

静态成员变量和方法,都没有this指针,不需要依赖对象调用。类中有静态成员变量时,必须要类内声明,类外定义(如果不赋初值,则默认就是0)。静态成员变量的一些用途有:可以在每次调用构造函数的时候,让静态成员变量+1,相当于可以计数使用。静态成员方法的一些用途:可以定义静态成员方法,直接使用类名+静态成员方法名访问静态成员变量,获得计数的个数。

普通成员方法: 编译器会添加一个this形参变量

  1. 属于类的作用域。
  2. 调用该方法时,需要依赖一个对象。
  3. 可以任意访问对象的私有成员,protected 继承 public private

static静态成员方法:编译器不会添加this形参变量

  1. 属于类的作用域。
  2. 可以使用类名作用域来调用方法。
  3. 可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他的static静态成员

常成员方法:

  1. 属于类的作用域。
  2. 调用该方法时,需要依赖一个普通对象(类型访问)或者常对象,如下代码所示。
  3. 可以任意访问对象的私有成员,只能读,不能写。
  4. 只要是只读操作的成员方法,最好实现为常成员方法,防止错误。
class Test
{
public:
	void func() { cout << "call Test::func" << endl; }
	static void static_func() 
	{ 
		cout << "call Test::static_func" << endl;
		cout << mb << endl;
		// cout << ma << endl; //错误,因为调用了非静态成员变量,静态成员函数不会产生this指针
	}
	void show() //Test *this
	{
		cout<<ma<<endl;
	}
	void show()const //const Test *this,构成函数重载,一般只读的方法设置为常成员方法
	{
		cout<<ma<<endl;
	}
	int ma;
	static int mb;//静态成员方法必须在类内声明,类外定义,初始化
};
int Test::mb = 30;

int main()
{
	const Test t();
	t.show();//不能调用show()方法,因为是调用的时候传入的是const Test *this,
	//而普通的show方法的参数是Test *this,不能将Test *this <- const Test *this转化
	//所以需要添加一个void show() const{}方法,也成为常方法,和void show()构成函数重载
	//加了const的show方法,传入的this指针式const的指针,所以参数列表不一样
}

指向类成员的指针

指向类成员(成员变量和成员方法)的指针。

指向成员变量的指针:

//指向类成员(成员变量和成员方法)的指针
class Test
{
public:
	void func() { cout << "call Test::func" << endl; }
	static void static_func() { cout << "call Test::static_func" << endl; }
	int ma;
	static int mb;//静态成员方法必须在类内声明,类外定义,初始化,不录入对象内存中
};
int Test::mb = 30;//如果不赋值的话那么编译器会默认给0

int main()
{	
	//int a = 10; int* p = &a; *p = 30; //可以通过普通的指针变量修改值
	//int Test::*p = &Test::ma; //但是对于普通成员而言,没有对象直接谈论其成员变量是没有意义的
	Test t1;
	Test* t2 = new Test();
	int Test::*p = &Test::ma;
	t1.*p = 20;
	cout << t1.*p << endl;
	t2->*p = 30;
	cout << t2->*p << endl;
	int* p1 = &Test::mb;//静态成员不依赖于类
	*p1 = 40;
	cout << *p1 << endl;
	delete t2;//释放空间
	return 0;
}

指向类的成员方法指针:

//指向类成员(成员变量和成员方法)的指针
class Test
{
public:
	void func() { cout << "call Test::func" << endl; }
	static void static_func() { cout << "call Test::static_func" << endl; }
	int ma;
	static int mb;//静态成员方法必须在类内声明,类外定义,初始化
};
int Test::mb = 30;

int main()
{	
	Test t1;
	Test* t2 = new Test();

	//C语言定义的函数指针严重性,无法从“void(__thiscall Test::*)(void)”转换为“void(__cdecl*)(void)
	//void(*pfunc)() = &Test::func;
	void(Test:: * pfunc)() = &Test::func;
	
	(t1.*pfunc)();
	(t2->*pfunc)();

	//static修饰的成员方法不依赖于对象,其地址就是普通地址,不需要加类作用域
	void(* psfunc)() = &Test::static_func;
	(*psfunc)();//静态方法不依赖于对象调用
	delete t2;
	return 0;
}

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

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

相关文章

el-tree通过default-expand-all动态控制展开/折叠

1、如下图通过勾选框动态控制展开/折叠&#xff0c;全选/清空 2、实现方式如下&#xff1a;定义key&#xff0c;监听checked2修改treeKey&#xff0c;重新渲染tere&#xff1b;附加全选和清空。 <div class"tree"><el-checkbox v-model"checked1"…

一场大火烧毁了印度的芯片梦 | 百能云芯

谈起印度的半导体发展史&#xff0c;鲜为人知的是&#xff0c;该国曾有可能成为全球半导体制造业的重要中心。然而&#xff0c;一个意外的事件彻底改变了历史进程&#xff0c;让印度错失了超越台积电的机会。 01半导体制造潜力 在高科技行业&#xff0c;也许很多人都不看好印度…

数据结构-栈和队列

目录 栈的概念 栈的使用 ​编辑 模拟实现栈 中缀表达式转后缀表达式 括号匹配 出栈入栈次序匹配 队列概念 队列的使用 栈的概念 栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作.进行数据插入和删除操作的一端称为栈顶,;另一端称为栈底.栈中的数据…

vue 获取设备指纹

import Fingerprint2 from fingerprintjs2 // async 异步请求 async getFingerprint () {return new Promise((resolve, reject) > {Fingerprint2.getV18({}, (result, components) > {resolve(result)})})}, // 获取用户sessionasync getSession () {/* 等待获取设备指纹…

微信小程序真机调试异常cmdId 1006, errCode-50011-已解决

cmdId 1006, errCode-50011 起因 小程序在模拟器上预览没问题,真机调试和体验版首页打不开,点展开显示cmdId 1006, errCode-50011 解决 查了下1006, 说是广告, 我没接广告,这个也不是错误码 1006广告组件被驳回你的广告正在被审核,无法展现广告后来找到几个类似的帖子…

uniapp的逆地理编码 和地理编码

1.先打开高德地图api找到那个 地理编码 2.封装好我们的请求 3.逆地理编码 和地理编码 都是固定的 记住自己封装的请求 就可以了 这个 是固定的 方式 下面这个是固定的 可以复制过去 getlocation就是uniapp提供的 获取经纬度 然后 下面的 就是高德地图提供的 方法 要想使用我…

短视频账号矩阵系统/技术开发搭建私有部署

本系统是基于短视频领域的新一代系统&#xff0c;旨在提供一个高效、全面的短视频管理与分发平台。系统采用先进的开发算法和技术&#xff0c;实现了智能化视频分类、推荐和用户互动功能。 目录 一、抖音SEO账号矩阵系统的开发和部署遵循以下原则&#xff1a; 二、账号矩阵绑…

【数据结构OJ题】链表的中间结点

原题链接&#xff1a;https://leetcode.cn/problems/middle-of-the-linked-list/description/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 快慢指针法 通过快慢指针找到中间结点&#xff0c;快指针每次走两步&#xff0c;慢指针每次走一步&#…

Spark MLlib机器学习库(一)决策树和随机森林案例详解

Spark MLlib机器学习库(一)决策树和随机森林案例详解 1 决策树预测森林植被 1.1 Covtype数据集 数据集的下载地址&#xff1a; https://www.kaggle.com/datasets/uciml/forest-cover-type-dataset 该数据集记录了美国科罗拉多州不同地块的森林植被类型&#xff0c;每个样本…

【ChatGPT 指令大全】怎么使用ChatGPT来辅助知识学习

目录 概念解说 简易教学 深度教学 教学与测验 解释一个主题的背后原理 总结 在当今信息时代&#xff0c;互联网的快速发展为我们获取知识提供了前所未有的便利。而其中&#xff0c;人工智能技术的应用也为我们的学习和交流带来了新的可能性。作为一种基于自然语言处理的人…

Labview控制APx(Audio Precision)进行测试测量(六)

用 LabVIEW 驱动 VIs生成任意波形 在 APx500 应用程序中&#xff0c;默认波形类型为正弦。这是指 APx 内置的正弦发生器&#xff0c;根据信号路径设置&#xff0c;许多测量还允许其他内置波形&#xff0c;如方波&#xff0c;分裂正弦波或分裂相位&#xff0c;以及使用导入的。w…

商城-学习整理-高级-全文检索-ES(九)

目录 一、ES简介1、网址2、基本概念1、Index&#xff08;索引&#xff09;2、Type&#xff08;类型&#xff09;3、Document&#xff08;文档&#xff09;4、倒排索引机制4.1 正向索引和倒排索引4.2 正向索引4.3 倒排索引 3、相关软件及下载地址3.1 Kibana简介3.2 logstash简介…

2023年国赛数学建模思路 - 案例:感知机原理剖析及实现

文章目录 1 感知机的直观理解2 感知机的数学角度3 代码实现 4 建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法&#xff0c;其…

C 语言的 ctype.h 头文件

C 语言的 ctype.h 头文件包含了很多字符函数的函数原型, 可以专门用来处理一个字符, 这些函数都以一个字符作为实参. ctype.h 中的字符测试函数如表所示: 这些测试函数返回 0 或 1, 即 false 或 true. ctype.h 中的字符映射函数如表所示: 字符测试函数不会修改原始实参, 只会…

构建高性能小程序:优化技巧和最佳实践

第一章&#xff1a;引言 随着移动互联网的快速发展&#xff0c;小程序成为了用户获取信息和进行业务交流的重要平台之一。然而&#xff0c;小程序由于受限于硬件资源和网络环境&#xff0c;开发者需要更加关注性能优化&#xff0c;以提供流畅、高效的用户体验。本文将介绍一些构…

DNS部署与安全详解(下)

文章目录 前言一、指定区域解析配置二、DNS服务器对外名称显示配置三、转发器使用配置四、配置辅助&#xff08;备份&#xff09;服务器五、如何让虚拟机可以真实上网六、为DNS服务器配置别名 前言 上一篇博客我们已经在Windows server2003的虚拟机上下载了DNS软件&#xff0c;…

流量日志分析--实操

[鹤城杯 2021]流量分析 <--第一道流量分析不难,主要就是布尔盲注的流量包分析,直接查看http请求包即可我们可以通过观察看到注入成功的响应长度不同,这里成功的为978字节,失败的994字节.不要问为什么.其实也可以直接判断.978的流量比994的少了非常多 显然就是成功的(因为这里…

HTML详解连载(2)

HTML详解连载&#xff08;2&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽超链接作用代码示例解释经验分享 音频标签代码示例注意强调 视频标签代码示例注意强调 列表作用&#xff1a;布局内容排列整齐的区域。分类&#xff1a;无序列表&#x…

kafka基本概念及操作

kafka介绍 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的 &#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各…

uniapp安卓ios打包上线注意事项

1、安卓包注意事项 隐私政策弹框提示 登录页面隐私政策默认不勾选隐私政策同意前不能获取用户权限APP启动时&#xff0c;在用户授权同意隐私政策前&#xff0c;APP及SDK不可以提前收集和使用IME1、OAID、IMS1、MAC、应用列表等信息 ios包注意事项 需要有注销账号的功能 3、安…
最新文章