无重复字符的最长字串-3
class Solution {
public:// 解决方法:双指针int lengthOfLongestSubstring(string s) { // 如果字符串为空,直接返回0if (s.length() == 0)return 0;// 如果字符串不为空,字符串每个字符都不同的情况下,最长字串的长度为1,所以Max初始化为1int left = 0, right = 0, Max = 1;//字符变量t用来存放每次两个指针范围内的后一个字符,用于与两个指针范围内的所有元素判断是否相同char t = 0;while (left < s.length()) {while (right < s.length() - 1) {//flag初始化为1用于标记,方便后续判断int flag = 1;//t赋值为两个指针范围内的后一个字符t = s[right + 1];//循环判断t是否与两个指针内有相同的字符for (int f = left; f <= right; f++) {//如果有相同字符flag标记为0,并break结束循环if (s[f] == t) {flag = 0;break;}}/*如果flag为零,说明双指针范围的后一个字符与双指针范围内有相同字符,后续的字符也没有必要判断了,break结束循环因为此情况下无论范围再怎么扩大(right++),双指针范围内总是有这个的重复字符,不符合题意。*/if (flag == 0)break;/*如果flag为1,说明双指针范围的后一个字符与双指针范围内没有相同字符,则把双指针范围的后一个字符括入到双指针范围内(right++)并与Max比较找到最大字串*/if (flag) {right++;Max = max(Max, right - left + 1);}}//内层while循环结束说明,字符串从头到尾已经遍历完,或者找到了相同字符break结束循环了//那么继续缩小左指针的范围(left),继续从左值针的位置开始查找最长字串,一直找到结尾left++;right = left;}return Max;}
};
每日问题
什么是 C++ 中的移动语义?它的作用是什么?
C++中的移动语义
移动语义是C++11中引入的一种特性,通过引入右值引用(T&&),支持资源的高效转移,而无需昂贵的拷贝操作。移动语义的核心作用是通过移动而不是复制的方式管理资源,从而提升程序性能,特别是在处理动态分配的资源(如内存、文件句柄等)时。、
移动语义的关键概念
1.右值引用(T&&):
是一个能够绑定到右值(临时对象)的引用类型。
它允许开发者直接访问右值对象的资源,并安全地将资源转移到其他对象中。
2.移动构造函数和移动赋值运算符:
是实现移动语义的两大工具。
它们负责接收一个右值引用参数,从而将资源从一个对象移动到另一个对象。
作用
1.提高性能:
在传递或返回大型对象时,通过移动语义可以避免昂贵的拷贝操作,直接转移资源的所有权。
常用于容器类(如std:vector)在插入、删除或交换元素时。
2.避免资源浪费:
临时对象通常会在作用域结束时被销毁,移动语义使得这些资源可以被重复利用,而不是浪费掉。
3.支持现代化C++编程:
与标准库(如std:move)结合,为开发者提供了更多灵活性和效率。
移动语义的实现
移动构造函数
class MyClass {
private:int* data;public:// 构造函数MyClass(int value) : data(new int(value)) {}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = nullptr; // 资源转移,确保原对象不再持有资源}// 析构函数~MyClass() {delete data; // 释放资源}
};
- 这是一个移动构造函数,接受一个右值引用 other(右值或临时对象)。
- 实现了资源转移:
- 将 other.data 的值转移给当前对象的 data。
- 将 other.data 置为 nullptr,避免原对象在销毁时重复释放资源。
- noexcept 表示此操作不会抛出异常,确保移动操作在某些场景(如标准容器的优化)中安全进行。
移动赋值运算符
class MyClass {
private:int* data;public:// 构造函数MyClass(int value) : data(new int(value)) {}// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {if (this != &other) { // 防止自我赋值delete data; // 释放当前资源data = other.data; // 转移资源other.data = nullptr; // 确保原对象不再持有资源}return *this;}~MyClass() {delete data;}
};
配合标准库使用
1.std::move
用于将一个左值显示转换为右值,从而启用移动语义。
常见用法时将一个临时对象的资源转移到另一个对象中。
示例:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1 的资源转移到 v2,避免拷贝
2.std::forward
通常用于完美转发函数模板参数,确保参数的值类别(左值或右值)正确传递。
使用场景
1.容器优化:
C++标准库容器(如std::vector、std::string)在插入或删除元素时,利用移动语义大幅减少不必要的拷贝。
2.返回值优化:
函数返回临时对象时,可以直接移动对象资源,避免临时对象的构造和销毁。
示例:
MyClass createObject() {MyClass obj(42);return obj; // 编译器自动应用移动语义
}
3.避免多余的动态分配:
动态资源管理类(如文件句柄、数据库连接)可以通过移动语义避免资源的重复分配和释放。
移动语义是 C++11 的重要特性,专注于高效的资源管理和性能优化。它通过右值引用、移动构造函数和移动赋值运算符,避免了不必要的拷贝操作,特别适合处理动态资源密集型任务。在现代 C++ 编程中,移动语义和标准库(如 std::move)的结合已经成为编写高效代码的重要工具。
右值引用是什么?如何使用右值引用实现移动语义?
右值引用是什么?
右值引用(T&&)是C++11引入的一种新类型引用,允许程序员直接操作右值(临时对象)。右值引用的主要目的是支持移动语义和完美转发。
右值与左值的区别
左值:表示一个内存中有确定存储地址的对象,可以被持久引用或操作。
例如:变量名、数组元素。
int a = 5;中的a是左值。
右值:表示临时对象或者表达式结果,通常无法直接获取地址。
例如:字面值、临时对象、函数返回的非引用置。
5、a + b的结果是右值。
右值引用可以绑定到右值上,与普通左值引用(T&)不同。
右值引用的语法
int&& r = 10; // r 是右值引用,绑定到字面值 10
int x = 20;
int&& r2 = x + 5; // r2 是右值引用,绑定到临时值 25
右值引用允许我们直接获取右值的资源,并通过移动语义避免拷贝操作。
右值引用和移动语义
移动语义的核定是将资源从一个对象“移动”到另一个对象,而不是“拷贝”它们。右值引用在实现移动语义时扮演了关键角色。
传统拷贝vs移动
1.传统拷贝:
创建一个对象的完整副本,可能会涉及动态资源的重新分配。
成本较高,特别是对动态内存的管理类(如vector)。
例如:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = v1; // 复制 v1 的内容,可能导致多次内存分配
2.移动:
通过右值引用,将一个对象的资源直接转移到另一个对象。
避免了不必要的内存分配和数据复制,提高性能。
示例:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1 的资源转移到 v2,v1 被置为“空”
如何使用右值引用实现移动语义
为了实现移动语义,需要提供移动构造函数和移动赋值运算符。
1.移动构造函数
移动构造函数接受一个右值引用作为参数,将资源从右值对象转移到当前对象。
示例:
class MyClass {
private:int* data;public:// 构造函数MyClass(int value) : data(new int(value)) {}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = nullptr; // 将原对象的资源置为空}// 析构函数~MyClass() {delete data; // 释放资源}
};
2.移动赋值运算符
移动赋值运算符转移资源时需要释放当前对象已有的资源(避免内存泄漏),然后接管右值对象的资源。
示例:
class MyClass {
private:int* data;public:// 构造函数MyClass(int value) : data(new int(value)) {}// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {if (this != &other) { // 避免自我赋值delete data; // 释放当前对象的资源data = other.data; // 转移资源other.data = nullptr; // 将原对象的资源置为空}return *this;}// 析构函数~MyClass() {delete data;}
};
完整示例:
#include <iostream>
#include <utility> // for std::moveclass MyClass {
private:int* data;public:// 构造函数MyClass(int value) : data(new int(value)) {std::cout << "Constructing: " << *data << std::endl;}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = nullptr;std::cout << "Moving: " << *data << std::endl;}// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {if (this != &other) {delete data;data = other.data;other.data = nullptr;std::cout << "Assigning: " << *data << std::endl;}return *this;}// 析构函数~MyClass() {if (data) {std::cout << "Destructing: " << *data << std::endl;delete data;} else {std::cout << "Destructing: nullptr" << std::endl;}}
};int main() {MyClass obj1(10); // 调用构造函数MyClass obj2 = std::move(obj1); // 调用移动构造函数MyClass obj3(20);obj3 = std::move(obj2); // 调用移动赋值运算符return 0;
}
输出:
Constructing: 10
Moving: 10
Constructing: 20
Assigning: 10
Destructing: nullptr
Destructing: nullptr
Destructing: 10
右值引用允许我们绑定到右值(临时对象),直接操作其资源。
移动语义通过右值引用实现,主要通过 移动构造函数 和 移动赋值运算符,实现高效的资源转移。
使用右值引用和 std::move 可以显著提升程序性能,特别是当类中包含动态资源(如内存、文件句柄)时。