1.简单介绍编码
utf_8变长编码,常用英文字母使用1个字节,对于其它语言可能2到14,大部分编码是utf_8,char_16是编码为utf_16, char_32是编码为utf_32, wchar_t是宽字符的,
utf_16是大小为俩个字节,所以牛马还有一个'\0'一个是3*2六个字节
utf_32是大小为四个字节所以大小就是3*4十二个字节
wchar_t 在window上通常为2字节,其它环境有不同大小。
cpp
char16_t str16[] = u"牛马";
char32_t str32[] = U"牛马";
wchar_t wstr[] = L"牛马";
cout << sizeof(str) << endl;
cout << sizeof(str16) << endl;
cout << sizeof(str32) << endl;
cout << sizeof(wstr) << endl;*/
2.对于拷贝优化
省略的部分是可以用上面的代替,先构造一个tmp出来,然后把s1复制到tmp上,再去交换s2和tmp,这里是浅拷贝了指针,如果不用交换而是直接把指针值复制过去会有野指针的问题,因为另一个被复制的变量如果被销毁了,那么它指向的空间也会被收回,浅拷贝是一个一个字节复制的,所以此时还剩一个变量指向被释放的空间就会有问题,而swap可以很好的解决,交换完后tmp销毁了,但是销毁的是s1的空间。
cpp
void test_string6()
{
string s1("hello world");
string s2 = s1;
cout << s1 << endl;
cout << s2 << endl;
string s3("xxxxxxxxxxxxxx");
s1 = s3;
cout << s1 << endl;
cout << s3 << endl;
}
cpp
string(const char* str = "")
{
_size = strlen(str);
// _capacity不包含\0
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s)
{
string tmp(s._str);
swap(tmp);
}
// 深拷贝问题
// s2(s1)
/*string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}*/
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
3.赋值运算符重载
先判断是否相等,是则直接结束,不一样就构造tmp并拷贝,然后交换,这里的if除了判断是否一样还有其它作用,{}符号表明了一个局部作用域,所以局部变量在出了这个{}会被销毁,tmp就会被销毁,一举两得。
cpp
//s1 = s3;
string& operator=(const string& s)
{
if (this != &s)
{
//string tmp(s._str);
string tmp(s);
swap(tmp);
}
return *this;
}
下面是进一步优化
在形参数部分变为string tmp,这样s3作为参数时,先构造tmp然后指向拷贝构造,这里swap只写tmp是因为还有一个隐式参数*this,所以交换,除了{}tmp销毁,代码简洁性高。
cpp
// s1 = s3;
string& operator=(string tmp)
{
swap(tmp);
return *this;
}
4.swap
可以看到模板的swap是先拷贝c,然后在赋值运算符重载俩次,注意如果a和b都有指向资源,那么就要用深拷贝,那么一次swap就三次深拷贝,是非常占资源的,所以用上面的就相对效率高,如果字节的类里面有,这里的std::swap调用的却是全局的swap,全局的swap效率高于模板swap(std::swap),而且有实例化的swap也不会去模板生成swap。是因为编译器会去找最适配的swap,就像你像吃辣条,你的妈妈却还是给你买健康的食品。
5.简单介绍引用计数和写时拷贝
引用计数:在加一个变量来记录有多少个指针指向这个空间,这样就有判断的标准,如果大于1就是有多个指向这块空间,等于一就说明只有一个指针指向这里,可以自由修改,也不会有多次析构。
写时拷贝:
写时拷贝是一种优化技术,通常用于减少内存使用和提高性能。它允许多个对象共享同一份数据,直到有一个对象需要修改这份数据为止。只有在需要修改时,才会进行数据的实际拷贝,从而避免不必要的内存分配和复制操作
6.vector简单使用
1.创建vector
创建俩个vector变量,vector是stl的模板要显式实例化,可以看到不写参数是走缺省参数的,而10,1就是10个int的元素,值为1。
用reserve(域string的作用一样,提前开空间),可以看到比原来大会改变,再改小则不会变化。
cpp
vector<int> v2(10, 1);
v2.reserve(15);
cout << v2.size() << endl;
cout << v2.capacity() << endl;
v2.reserve(25);
cout << v2.size() << endl;
cout << v2.capacity() << endl;
v2.reserve(5);
cout << v2.size() << endl;
cout << v2.capacity() << endl;
2.使用resize
resize函数会改变vector实例对象的大小,当为5时就只有五个在vector里面,
这里是如果扩大则会填充第二个参数,一开始是10个1,后面变成15,则再填5个2,25就再填10个3.
cpp
void test_vector3()
{
//TestVectorExpand();
vector<int> v(10, 1);
v.reserve(20);
cout << v.size() << endl;
cout << v.capacity() << endl;
v.resize(15, 2);
cout << v.size() << endl;
cout << v.capacity() << endl;
v.resize(25, 3);
cout << v.size() << endl;
cout << v.capacity() << endl;
v.resize(5);
cout << v.size() << endl;
cout << v.capacity() << endl;
}
3.二维的vector
先初始化一个一维的v,然后以v为参数构造二维的vv,
cpp
vector<int> v(10, 1);
vector<vector<int>> vv(5,v);
4.迭代器和范围for遍历vector
这里前置++和前置--则最后只有八位打印出来,因为是从begin后一位和end前一位,循环和范围for也可以逐个打印,像之前的数组一样。
cpp
void test_vector1()
{
vector<int> v1;
vector<int> v2(10, 1);
vector<int> v3(++v2.begin(), --v2.end());
for (size_t i = 0; i < v3.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
vector<int>::iterator it = v3.begin();
while (it != v3.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
}
5.vector的操作
insert是在vector中插入数据,前一个是位置,后一个是插入什么元素,后面用for来cin初始化vector。
cpp
vector<int> v(10, 1);
v.push_back(2);
v.insert(v.begin(), 0);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
v.insert(v.begin() + 3, 10);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
vector<int> v1(5, 0);
for (size_t i = 0; i < 5; i++)
{
cin >> v1[i];
}
for (auto e : v1)
{
cout << e << ",";
}
cout << endl;
前面知识补充:
在 C++ 中,可以使用强制转换将指针类型转换为 void*,这是合法的,且在某些情况下非常有用。下面,我将详细解释如何在 C++ 中使用 void*,以及在与 std::cout 结合使用时的注意事项。
- 强制转换为 void*
在 C++ 中,任何指针类型都可以被隐式或显式转换为 void*。void* 是一种通用指针类型,能够指向任何类型的数据。通过这种转换,你可以在不知道指针具体类型的情况下处理内存地址。这在处理底层内存操作或需要泛型编程的情况下特别有用。
- 与 std::cout 的结合使用
如你所述,std::cout 对 const char* 类型进行了特别的处理,重载了 << 运算符以打印字符串的内容。如果你需要打印 const char* 的地址,可以使用 (void*) 来强制转换:
std::cout << (void*)cStr;
这将输出指针的内存地址,而不是它指向的字符串内容。
- 示例代码
以下是一个完整的示例代码,演示了如何使用 void* 强制转换以及打印字符串内容和指针地址的区别:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, World!";
const char* cStr = str.c_str();
// 输出字符串内容
std::cout << "内容: " << cStr << std::endl; // 输出: Hello, World!
// 输出指针的地址
std::cout << "指针地址: " << (void*)cStr << std::endl; // 输出: 指针的内存地址
return 0;
}
- 输出结果
运行上述代码时,输出将类似于:
内容: Hello, World!
指针地址: 0x7ffee6b7c3d0 // 这个值会因每次运行而不同
- 关键点总结
1.重载运算符:std::cout 对 const char* 类型的重载实现允许直接输出字符串内容。
2.地址输出:如果你想输出指针的地址,必须使用 (void*) 来进行强制转换。
3.合法性:使用强制转换 void* 是合法的,可以用于指针类型与通用指针之间的转换。
- 注意事项
虽然强制转换为 void* 是合法的,但在某些情况下需要谨慎使用:
4.类型安全:使用 void* 可能会导致类型安全问题,尤其是在进行指针运算时。
5.需要显式转换回原始类型:在使用 void* 时,如果需要再次使用原始类型的指针,你必须显式地将其转换回来,这可能会引入错误。
总的来说,使用 void* 是 C++ 中一种灵活的指针处理方式,但要注意相应的安全性和可读性问题。
在 C++ 中,std::string 类提供了 replace 函数,用于替换字符串中的部分内容。这个函数可以使用不同的参数形式,允许你根据需求替换子字符串或单个字符。std::string::replace 函数的基本用法
- 替换子字符串
使用 std::string::replace 来替换指定范围内的字符,可以按以下方式调用:
std::string& replace(size_t pos, size_t len, const std::string& str);
1.pos:要替换的起始位置。
2.len:要替换的字符数。
3.str:用于替换的字符串。
示例代码:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, world!";
// 从位置 7 开始替换 5 个字符
str.replace(7, 5, "C++");
std::cout << str << std::endl; // 输出: Hello, C++!
return 0;
}
- 替换单个字符
std::string::replace 也可以用来替换单个字符,示例如下:
std::string& replace(size_t pos, size_t len, size_t n, char c);
4.n:替换字符的数量。
5.c:要替换成的字符。
示例代码:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, world!";
// 从位置 7 开始替换 5 个字符为字符 '*'
str.replace(7, 5, 3, '*');
std::cout << str << std::endl; // 输出: Hello, ***d!
return 0;
}
- 替换另一个字符串
你还可以用另一个字符串的子串来替换当前字符串中的部分内容:
std::string& replace(size_t pos, size_t len, const char* s);
示例代码:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, world!";
// 从位置 7 开始替换 5 个字符为 C 风格字符串 "C++"
str.replace(7, 5, "C++");
std::cout << str << std::endl; // 输出: Hello, C++!
return 0;
}
- 替换多个字符
如果需要替换多个字符,可以使用 std::string::replace 结合字符串的长度来实现:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, world! Hello, world!";
// 替换所有 "world" 为 "C++"
size_t pos = 0;
std::string to_replace = "world";
std::string replace_with = "C++";
while ((pos = str.find(to_replace, pos)) != std::string::npos) {
str.replace(pos, to_replace.length(), replace_with);
pos += replace_with.length(); // Move past the replaced part
}
std::cout << str << std::endl; // 输出: Hello, C++! Hello, C++!
return 0;
}
总结
std::string::replace 是一个强大的工具,可以灵活地对字符串中的部分内容进行替换。根据需要,可以选择替换字符、字符串或使用 C 风格字符串。通过适当的循环和查找,可以实现更复杂的替换操作。