C++ 构造函数-2
构造函数-2
构造函数体赋值
在对象创建的时候,编译器会调用构造函数,给对象当中的成员赋一个合适的初始值。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
但是其实本质上上述的操作并不是给成员初始化,只能说是,给成员赋值,整整的初始化只能初始化一次,而在构造函数体内能给成员多次赋值。
所以这时候我们就在构造函数中使用初始化列表。
初始化列表
初始化列表:以一个冒号开始,数据成员之间用 逗号进行分割,其中的每个"成员变量"后面跟
一个放在括号中的初始值或表达式。
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
如上图所示,数据成员后面的括号当中就是给这个数据成员初始化值。
看似这个初始化和 赋初值,看似差不多,但是在有些情况下,初始化列表才能解决。
【注意】:
- 每一个成员最多只能再初始化链表当中出现一次。
- 有些成员必须在初始化列表初始化
上述必须初始化的成员有 :
- 引用成员变量
- const成员变量
- 自定义成员类型(当其类中没有默认构造函数)
比如我们直接使用构造中 直接赋初值,来初始化这个 const 的成员:
像上述就报了,必须初始化常量限定····这个错,这就代表着要在 定义的时候初始化,那么我们在赋值的时候是不行的。
那么我们就理解实质了,也就是说,如我们不使用初始化列表,那么我们在构造函数中的初始化就是直接 赋初值,那么这种方式是不能初始化 在定义的时候就需要初始化的成员的。
也就是说,在C++当中,这个位置不认为是初始化的地方:
如上图,这个只是赋值。
在这个位置才认为是 初始化。
我们之前说过,当我们初始化一个成员的时候,如果是内置类型,不进行处理:
但是也不是都不处理,如果这个内置类型,在类当中是定义给了值的:
这个不是初始值,这个是 给 初始化列表的缺省值,也就是说,如果我们不对这个内置类型进行处理,那么这个x 就会被 赋 1 (缺省值)这个值,如果我们在初始化列表当中对这个x 进行了赋值,那么这个x 就是 在初始化列表当中初始化的值。
同样,如果对象是 没有默认构造函数,那么我们在创建这个对象的时候,就会报错:
如上图,我们在B类当中创建一个了一个 A类的对象,但是如下图所示,(没有缺省参数)而我们创建的是无参的构造函数来创建这个对象的,我们构造函数当中没有默认构造函数,那么就会报错:
当我们在A 的构造函数中 的 初始化列表当中去的 调用这个对象的构造函数,去创建这个对象,那么就不会报错:
而且我们发现,成功赋值。
所以我们建议我们能使用初始化列表,就用初始化列表,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表来进行初始化,因为初始化列表是这个些成员定义的地方,但是也不是意味着所有的初始化都可以用初始化列表。
class Stack
{
public:Stack(int Top, int capacity):_a((int*)malloc(sizeof(int))),Top(Top),capacity(capacity){if (_a == NULL){perror("malloc fail");exit(-1);}// 初始化数组memset(_a, 0, sizeof(int) * capacity);}protected:int* _a;int Top;int capacity;
};
比如上述例子,我们在实现栈的构造函数的时候,我们希望在 初始化列表当中去 初始化 _a 指针指向一块空间。那么我们是可以实现的。
但是,我们知道,malloc函数是有可能会开辟空间失败的,所以我们要进行判断,但是这个判断在是在 初始化列表当中不能做到的,我们就只能再的 构造函数中实现,那既然有这样的场景,那么假设我们还想初始化我们创建的数组,那么我们还需要再使用这个函数来进行处理,如上述例子一样。
这些都是初始化列表不能实现的,而且,这种情况很多时候不止一种,可能会有很多行,我们这里想表达的意思是,总有一些工作时初始化列表做不完的。那么我们就可以在函数体当中去实现。
再比如我们要动态开辟二维数组,那其中必然就有一个循环,这也是初始化列表不能实现的。
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后
次序无关。
如下例子:
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
上述例子,输出的不是 1 1 ,而是 1 随机值。
因为上述例子,我们是声明的 _a2 这个变量,那他就会先初始化,那么我们在初始化列表当中写的是 _a2(_a1) 此时 _a1 还没有会被初始化,所以是随机值。先走 _a2(_a1) 再走 _a1(a)。
上述例子还不是很严重,我们在之前实现栈的构造函数的时候,如果想下述这样写就会出错了:
我们发现上述在初始化 _a 的时候,capacity是没有初始化的,也就是说现在 的 capacity 是一个随机值,那么我们以一个随机值来开辟空间大小,就会出现很严重的问题。
所以,我们一般把声明的顺序和初始化的顺序保持一致。
explicit关键字
隐式类型转换
例子:
B b(10); //1B c = 10; //2
代码1是在调用 B 这个类的构造函数来创建这个对象,那么 代码2是在干嘛呢?
我们发现他是一个隐式的类型转换,由整形类型转化成自定义类型。
他是先用 10 ,利用 B 的构造函数来创建一个 临时对象,这个临时对象的类型就是 自定义类型B,然后 c 再用 B 当中的拷贝构造函数去 把 临时对象,拷贝给 c 这个对象。
我们发现,上述即调用了 构造函数,还调用了 拷贝构造函数,但是编译器不想这样做,他不像上面这个代码,即调用构造函数有调用拷贝构造函数,所以,一遍像上述情况,编译器会进行优化。
像上述例子,他会直接使用 10 这个整型数据来进行 构造。
我们把 B的拷贝构造写出来,其中加入打印,看看构造函数和 拷贝构造函数是否被调用:
class B
{
public:B(int n):_n(n)//,aobj(10){cout << "B::B(int)" << endl;}B(const B& b):_n(b._n){cout << "B::B(const B& b)" << endl;} protected:const int _n;
};int main()
{B b(10); //1B c = 10; //2return 0;
}
输出:
我们发现,拷贝构造函数并没有被调用,此处就是被编译器优化了,如果按照本来的实现过程,应该还有临时对象的创建。
构造函数和 拷贝构造函数都是 构造函数,除了老的编译器,现在的编译器一般都不会容忍在同一个表达式当中重复的调用构造函数。
上述例子也是,只要是在一行当中的重复的调用构造函数都会被优化。
上述两个例子,ra1 会 优化 ,ra2 不会优化,我们可以看到,两种情况下,ra2 的代码明显大多了,还需要调用 赋值重载函数。
我们现在来举一个反例,来验证我们刚刚说个,创建临时对象这一过程:
B& pb1 = 2; //代码1
pb1 去引用之前的 b 和 c 都是可以的 ,但是,我们上述引用的是一个整形 2 ,这就不行了,报错:
但是如果我们把这个引用转换成 const 的就可以了:
const B& pb2 = 2; //代码2
我们发现上述代码编译通过了。
我们上述定义的是 B 类型的引用类型,编译器在这时候就不能在进行优化了,上述代码的实现过程就是我们之前说的,创建一个 临时对象来进行 赋值,像上述的代码2,就是用2 ,调用构造函数创建了一个 临时对象,这里的 pb2 引用的就是 这个临时对象。
而临时对象具有常性,所以,之前的报错是 也 我们的引用类型不是 const 所修饰的引用,当我们用const 修饰之后就可以编译通过了。如下图所示:
此时我们运行这个代码,输出:
我们发现只调用了一次构造函数。
那么像上述的这种创建一个临时对象来让一个自定义类型,接收一个不是这个类型的值,然后来创建这个对象,主要是为了实现像下述代码这种情况:
class list
{
public:list(const string& st){}
};int main()
{string name1("李四");list LS1(name1); // 代码1list LS2("李四"); // 代码2return 0;
}
上述代码 1 和 代码 2 ,肯定是 代码2 的方式比较方便,代码能实现这种情况的是因为,“李四” 这个字符串就和 const String& 之间就发生了 隐式类型转换,和之前的 const B& pb2 = 2; 是一样的。
当我们 不加 const修饰就报错了:
explicit关键字
根据上述的 list 类的描述,我们知道:构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用
那么我们在自定义类型,实现构造函数的时候,我们就可以在函数名之前加一个 explicit 这个关键字,来防止类似于 B b = 10 这样的 隐式类型转换:
Static 成员
假设我们现在想要,记录当前我们实现的类,到底创建了多少个对象,那么我们可以在 全局当中定义一个变量(scount),用这个变量来计算我们创建了多少个 对象;当我们调用这个类当中的 构造函数或者是拷贝构造函数的时候,我们就 scount++;如果我们调用的是 析构函数就 scount--。从而来记录,当前有多少个这个类的对象。
static int _scount = 0;class A
{
public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }
};void func(A aa)
{cout << __LINE__ << " : " << _scount << endl;return;
}int main()
{cout << __LINE__ << " : " << _scount << endl;A aa1;A aa2;func(aa2);cout << __LINE__ << " : " << _scount << endl;
}
输出:
我们上述使用 __LINE__ 这个宏来打印当前行数,上述就输出了 每一行代码的情况下当前有多少个对象。
当然,不管你在 局部,全局,还是在 对象当中去创建这个对象,只要是创建对象都需要构造函数或者是 拷贝构造函数,那么只要调用,计数器就会++;只要调用析构函数,计数器都会 --。
如上述,我们在函数中创建的 对象,在对象当中打印这个 计数器,发现是3,说明当前已经创建这个局部的对象,但是在函数调用结束之后,对象会调用析构函数进行销毁,那么计数器就会--,所以我们在主函数当中打印的计数器的值是 2。
但是,我们上述使用的全局变量,这样做不太好,所以我们在定义在定义类型的时候,可以对类似这样需要全局实现的 变量,进行封装,把这边变量封装到 自定义类型当中。
Static 成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用
static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
这里我们要搞清楚,普通成员变量和 静态成员变量,这两个的区别,普通成员属于类的当前对象,而 静态成员变量属于的是类的每一个对象,因为是静态的,他的生命周期是全局的;而普通成员的生命周期是 这个对象的生命周期。
因为 静态成员变量是属于全局的,所以我们在 对象当中,利用构造函数去初始化是不行的:
如上图,报错了。
静态成员变量只能再 全局位置 ,类外面进行 定义。像下面这样定义这个静态成员变量。
int A::_scount = 0;
那么像我们之前实现的 _scount 是 protected 的,那么我们就不能直接 A::_scount 这样访问了,除非这个 _scount 是 public 的。
当然为了封装,我们一般不会是 共有的,一般是私有的,public的和 全局的每什么区别,达不到封装的效果。
所以为了取到 不是 public的 静态成员变量,我们就创建一个 静态成员函数,来取到 其中的 静态成员变量。
静态成员函数没有隐藏的this指针,不能访问任何非静态成员
static int Get_Scount(){return _scount;}
因为这个静态成员函数不能访问非静态的成员,他能帮我们取到 静态的成员的值,所以我们使用这个函数来取到数据,又不会破坏封装性。
class A
{
public:A() { ++_scount;}A(const A& t) { ++_scount; }~A() { --_scount; }static int Get_Scount(){return _scount;}protected:static int _scount;
};int A::_scount = 0;void func(A aa)
{cout << __LINE__ << " : " << A::Get_Scount() << endl;return;
}int main()
{cout << __LINE__ << " : " << A::Get_Scount() << endl;A aa1;A aa2;func(aa2);cout << __LINE__ << " : " << A::Get_Scount() << endl;
}
我们可以通过 " . " 或者是 " 类名:: " 来访问静态成员函数。
静态成员变量是不能给缺省值的,因为静态成员变量的初始化不在 初始化列表当中,而是在 全局当中。
关于静态和非静态的关系:
同类中 非静态可以调用非静态,而静态 只要是不受指定类域,和访问限定符的限定就可以访问。
因为 访问非静态的成员函数的访问,需要 this 指针,而在静态成员函数中没有 this 指针。
设计一个类只能在 栈/堆上创建对象
如下,在C++当中的对象存储的位置:
class A
{
public:protected:int _a = 1;int _b = 1;
};int main()
{static A aa1; // 静态区A aa2; // 栈A* ptr = new A; // 堆return 0;
}
我们发现,我们创建对象的地方有好几个,而且像上述一样实现的类,用户可以在 静态区 栈 堆 上随便选个位置来创建对象。
我们可以把 构造函数 的访问权限 设置为 protected 私有的 ,这样我们在类外部就不能随便去调用这个构造函数,去创建对象。
那么问题来了,既然是私有的,那么我们在类外部怎样去 调用这个 构造函数 呢?
我们可以定义 public 的 成员函数,既然在来外面不能调用,那么我们就在 类当中去调用构造函数,如下所示:
这样我们可以通过调用这些函数 来在对应的 位置创建对象。
但是,向上述还是有问题,既然这个函数是成员函数,那么我们如果在类外面访问这个函数呢?
我们如果想访问成员函数必须通过对象来访问,但是现在,我们不能创建对象。
这时候,静态的成员函数就可以帮我们解决这个问题,因为静态的成员函数就可以在类外面进行调用,他是全局的,不在类当中。
class A
{
public:static A GetStackA(){A aa;return aa;}static A* GetHeapA(){return new A;}protected:int _a = 1;int _b = 1;
};
如上述这种,我们就可以只创建对应的函数,来达到只能再某一个区域创建对象的这种限制。
例题:
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
这里就可以利用这个 静态成员,通过调用n次构造函数来,计算出从1 加到 n 的值。这里我们想到创建一个这个类的数组,数组元素个数为 n。
class Sum
{
public:Sum(){_sum += _i;_i++;}int GetSum(){return _sum;}protected:static int _sum;static int _i;
};int Sum::_sum = 0;
int Sum::_i = 1;class Solution {
public:int Sum_Solution(int n) {Sum a[n];return a[0].GetSum();}
};
相关文章:

C++ 构造函数-2
构造函数-2 构造函数体赋值 在对象创建的时候,编译器会调用构造函数,给对象当中的成员赋一个合适的初始值。 class Date { public: Date(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _month; i…...

网安笔记 08 key management
Key Management —— 不考 网络加密方法 1.1 链路加密 特点: 两个相邻点之间数据进行加密保护 不同节点对密码机和Key不一定同中间节点上,先解密后加密报文报头可一起加密节点内部,消息以明文存在密钥分配困难保密及需求数量大 缺点&…...

Linux socket
百度百科对于Socket的介绍 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信…...

14.构造器的排序分组.子查询
学习要点: 1.排序分组 2.子查询 本节课我们来开始学习数据库的构造器查询中的子查询、排序、分组等。 一.排序分组 1. 使用 whereColumn()方法实现两个字段相等的查询结果; //判断两个相等的字段,同样支持 orWhereColumn() //支持…...

【剑指 Offer】05,替换字符创中的空格;难度等级:简单。易错点:C++中 char 和 string 类型的转换
【剑指 Offer】05,替换字符创中的空格;难度等级:简单。 文章目录 一、题目二、题目背景三、我的解答四、易错点五、知识点:char 和 string 类型的转换 一、题目 二、题目背景 在网络编程中,如果 URL 参数中含有特殊字…...

图像分割入门教程
文章目录 图像分割入门教程1. 图像分割基本概念2. 基于阈值的图像分割3. 基于区域的图像分割4. 基于边缘的图像分割5. 基于区域和边缘的图像分割区别6. 基于深度学习的图像分割7. 实现步骤结论 图像分割入门教程 图像分割是计算机视觉领域的一个重要任务,其目标是将…...

C++入门教程||C++ 信号处理||C++ 多线程
C 信号处理 C 信号处理 信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 CtrlC 产生中断。 有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于…...

java计算矩形的面积和周长的方法
在生活中,我们常常需要计算某个矩形的面积和周长,如我们经常用的计算器就是个不错的选择,它可以计算出任意一个矩形的面积和周长。那么,如果你想使用 Java编程语言来计算矩形的面积和周长,你该如何做呢?今天…...

一分钟掌握如何更换Jupyter Notebook的主题和字体
Jupyter Notebook 更换主题(背景、字体) 在现代科技发展的浪潮中,Jupyter Notebook 作为一种强大的交互式笔记工具,已经被越来越多的用户所使用。它以其简单易用、功能强大、资源丰富等特点,成为了许多人学习、工作、科…...

如何系统全面的自学自动化测试?明确后我直接拿到了20K
玩自动化测试多年的老司机带你上车全面系统学习自动化测试,并且还能教你如何学习才能在今年拿到一份不错的offer。 说到系统全面,就是以目前绝大部分公司招聘要求的知识内容为基准,毕竟我们学习自动化测试都是为了高薪工作,《史记…...

【搭建私有云盘】无公网IP,在外远程访问本地微力同步
文章目录 1.前言2. 微力同步网站搭建2.1 微力同步下载和安装2.2 微力同步网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 私有云盘作为云存储概念的延伸,虽然谈不上多么新颖,但是其…...

Pytest自动化测试框架一些常见的插件
Pytest拥有丰富的插件架构,超过800个以上的外部插件和活跃的社区,在PyPI项目中以“ pytest- *”为标识。 本篇将列举github标星超过两百的一些插件进行实战演示。 插件库地址:http://plugincompat.herokuapp.com/ 1、pytest-htmlÿ…...

【力扣】刷题+剑指offer第二版
文章目录 题目收藏不含重复字符的最长子串最长公共子串 剑指 Offer剑指 Offer 05. 替换空格剑指 Offer 03. 数组中重复的数字剑指 Offer 04. 二维数组中的查找剑指 Offer 09. 用两个栈实现队列剑指 Offer 07. 重建二叉树剑指 Offer 06. 从尾到头打印链表剑指 Offer 11. 旋转数组…...

QueryStorm Crack
QueryStorm Crack 应用程序现在可以指定“minRuntimeVersion”。 添加了用于节流和API密钥管理的HTTP请求基础结构(请求/尝试/重试循环)。 改进了许可提示的处理(避免在多个单元格中评估许可功能时出现多个提示)。 已添加“IDialogServiceExt”接口,该接口允许应用程…...

网络安全与隐私保护:挑战与应对策略
一、引言 在互联网时代,个人隐私保护已经成为一项全球性的难题。尤其是在“裸奔”时代下,人们越来越难以避免个人隐私泄露的风险。网络安全与隐私保护已经成为了人们关注的焦点。保护网络隐私已经成为了每个人最基本的权利和义务。 二、网络安全与隐私…...

不同应用场景瑞芯微RK3568主板方案定制
随着物联网和智能设备的迅猛发展,瑞芯微RK3568主板方案作为一种高性能的系统System-on-a-chip(SoC),已经成为嵌入式系统、智能家居设备和工业自动化设备等应用场景的首选方案。定制瑞芯微RK3568主板方案可以满足不同应用场景的需求…...

公司数字化转型,如何选择高效的知识管理工具?
随着企业数字化转型的加速,知识管理工具的重要性也日益凸显。好的知识管理工具可以帮助企业提高工作效率、降低成本、提高创新能力和竞争力。但是,市场上的知识管理工具繁多,如何选择高效的知识管理工具成为了企业面临的一大难题。本文将从以…...

银行从业法律法规(初级)-多选
目录 前言一、巴塞尔相关1-1 第一版巴塞尔1-2 第二版巴塞尔1-3 第三版巴塞尔 二、银行2-0 银行相关2-1 中国人民银行2-2 国家开发银行2-3 政策性银行2-4 银保监会2-5 银监会 三、合规&风险3-1合规3-2 风险3-3 资产负债管理 四、货币&财政4-1 货币4-2 利率 五、存款贷款…...

Maven 依赖管理 学习
目录 Maven 依赖管理 可传递性依赖发现 依赖范围 依赖管理 Maven 自动化部署 问题描述 解决方案 修改项目的 pom.xml Maven Release 插件 Maven Web 应用 创建 Web 应用 构建 Web 应用 部署 Web 应用 Maven 依赖管理 Maven 一个核心的特性就是依赖管理。当我们处…...

分享105个NET源码ASP源码,总有一款适合您
分享105个NET源码,总有一款适合您 源码下载链接:https://pan.baidu.com/s/1zFMIHX6juXdR2CaHMEr5mQ?pwdf5hz 提取码:f5hz 下面是文件的名字,我放了一些图片,文章里不是所有的图主要是放不下...,大家下载后…...

Web缓存利用分析(三)
导语:前一篇文章介绍了Server Cache Poisoning在实际应用场景下,产生DOS攻击的利用方式。本篇文章则介绍Web Cache Deception在真实场景下的应用方式和测试情况。 前言 前一篇文章介绍了Server Cache Poisoning在实际应用场景下,产生DOS攻击…...

Git合并冲突的根本原因和解决方法
假如您现在正在参与一个团队项目,并取得了实质性的进展。然而,当你准备提交代码的时候,发现团队中的某个人也更改了同一个文件,并且先你一步提交了——您现在遇到了代码冲突问题。而且需要花时间去解决自己的更改与别人的更改之间…...

从C语言到C++⑨(第三章_CC++内存管理)详解new和delete+面试题笔试题
目录 1. C语言动态内存管理 1.1 C/C内存分布 1.2 C语言中动态内存管理的方式 2. C动态内存管理方式 2.1 new/delete操作内置类型 2.2 初始化new数组的问题 2.3 new 和 delete 操作自定义类型 3. operator new与operator delete函数详解 3.1 operator new与operator de…...

阿里云服务器安装宝塔Linux面板教程图解
使用阿里云服务器安装宝塔面板教程,阿里云服务器网以CentOS操作系统为例,安装宝塔Linux面板,先远程连接到云服务器,然后执行宝塔面板安装命令,系统会自动安装宝塔面板,安装完成后会返回面板地址、账号和密码…...

ORA-01555 ORA-22924 快照过旧问题处理
ORA-01555 ORA-22924 快照过旧问题处理 问题描述 使用数据泵导出数据,或在业务功能查询某个表时,可能出现 ORA-01555 ORA-22924 快照过旧的错误: ORA-01555: snapshot too old: rollback segment number with name "" too small…...

Win11系统更新后网络速度变的很慢怎么办?
Win11系统更新后网络速度变的很慢怎么办?有用户将自己的电脑系统升级到了Win11之后,出现了一些问题。电脑在使用中出现了网络速度变慢的情况。而且其它的设备在连接网络后速度是正常的,那么这个问题要怎么解决?来看看以下的方法分…...

了解 XML结构(一)
文章目录 1 XML定义2 了解XML结构3 XML节点类型4 加载读取XML5 小结 1 XML定义 XML是一种可扩展标记语言(Extensible Markup Language, XML),可以用来标记数据,定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 …...

Vue简单语法记录
指令 v-show:展示和隐藏 如图片的展示和隐藏 (底层是其实已经创建了 加了个css属性,display none)v-if:创建和删除 创建和删除,删除就真的没了v-for: 遍历指令 v-for"item in list&…...

matplotlib的安装和使用教程:中文字体及语言参数设置
matplotlib是一个常用的数据可视化库,广泛应用于科学研究、工程设计、金融分析等领域。由于其强大的功能和易用性,matplotlib已经成为了广大科研工作者和数据分析师的必备工具之一。本文将重点介绍matplotlib的安装和允许中文及几种字体的方法。 一、mat…...

mysql深分页
第一种:主键自增id情况: 未改: select * from wx_product_category_info where category_name_cn#{categoryNameCn} and category_type#{categoryType} order by id asclimit #{pageNum}, #{pageSize};在普通的limit条件下,如果…...