C++字符串和vector

C++的字符串是一个对象,存在于std标准库中,是std标准库提供的自定义类类型
所占存储空间较大,40字节,数据成员一般都包含
在这里插入图片描述
vector是一种动态数组,也存在与std标准库中,一般都有size和capacity两个数据成员

字符串创建方式

默认创建

string s1;

C语言字符串创建

string s2("Hello");

指定长度的单个字符创建

string s3(10,'a');

字符串拼接

只能用+拼接,+两边必须有一个是string类对象

string s4 = s3 + s2;
s4 = s3 + "Hello";
s4 = "Hello" + s3;

字符串追加

追加特定长度字符

s3.append(3,'a');

追加字符串

s3.append("Hello");

追加string类对象

s3.append(s2);

追加string类对象指定起始位置指定长度

s3.apend(s2,0,5);

字符串截断

//参数1,pos,指定起始位置,参数2,len,指定长度

cout<<s3.substr(0,3)<<endl;

auto

C++的一种自动遍历模式,auto是自动推导,如果不带引用可能会浪费空间

for(auto& t: s3)
{
cout<<t<<' ';
}

完整代码

#include <iostream>

using std::cout;
using std::endl;
using std::string;
//std::string 标准库提供的自定义类类型

void sp(string& s)
{
cout << "s:" << s << endl;
cout << "s:sizeof:" << sizeof(s) << endl;
cout << "s.size:" << s.size() << endl;
cout << "s.length:" << s.length() << endl;

cout << endl;
}
int main()
{
//三种构造函数
string s1;
string s2("Hello");//c -to c++
string s3(10, 'a');

sp(s1);
sp(s2);
sp(s3);

//字符串拼接
string s4 = s3 + s2;
s4 = s3 + "Hello";
s4 = "Hello" + s3;
sp(s4);
//操作运算符两边必须有一个是string对象

//追加
s3.append(3, 'a');//追加字符
sp(s3);
s3.append("Hello");//追加字符串
s3.append(s2);//追加string对象
s3.append(s2,0,5);//追加特定起始位置特定长度的string对象
sp(s3);

//截断
cout << s3.substr(0, 3) << endl;//参数一pos,参数2len

cout << &s1 << endl;//对对象取地址,得到是对象首地址,而不是字符串内容首地址

cout << endl;

//auto是自动推导,&是引用,没有引用会浪费内存
for (auto& t : s3)
{
cout << t << ' ';
}
return 0;
}

vector创建方式

默认创建

vector<int> numbers;

初始化创建

vector<int> numbers(10,0);//创建大小为10的数组,数组元素皆为0,vector容量为10

vector操作

添加元素

numbers.push_back(1);

删除末尾元素

numbers.pop_back();

代码实现

#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

void vp(vector<int>& numbers)
{
cout << "numbers:size:" << numbers.size() << endl;
cout << "numbers.capacity:" << numbers.capacity() << endl;

}
int main()
{
vector<int> numbers;
vp(numbers);

for (int i = 0; i < 10; ++i)
{
numbers.push_back(i);
vp(numbers);
}

//初始化操作
vector<int> a(11, 0);
for (auto& t : a) cout << t << ' ';

vp(a);

return 0;
}

有一个需要我们值得注意的地方
vector对象在size为capacity时且还需要再添加新的元素时,会进行扩容

扩容步骤
申请一个原先x倍的空间
将旧空间的元素拷贝到新空间中
销毁旧空间,vector数组指向新空间

不同编译器在扩容时的倍率不同
在这里插入图片描述
在这里插入图片描述
可以看到,一般的编译器扩容一般都是2倍扩容,而vs会以原来的1.5倍扩容

new与delete重构

new与delete的工作步骤

new:

1.调用operator new标准库函数申请未定义的空间
2.在该空间调用构造函数初始化对象
3.返回一个相应类型的指针
形式 void* operator new(size_t)

delete

调用析构函数
调用operator delete标准库函数回收对象空间

代码实现

#include <iostream>
#include <cstdlib>
#include <cstring>

using std::cout;
using std::endl;

class Student
{
public:
Student(const char* name,const int& id)
:_name(new char[strlen(name) + 1]())
,_id(id)
{
strcpy(_name,name);
cout<<"Have done create"<<endl;
}

void release()
{
delete[] _name;
_name = nullptr;
}
~Student()
{
if(_name) release();
cout<<"~Student"<<endl;
}

void print() const
{
cout << "name:" << _name << endl;
cout << "id:" << _id << endl;
}

void* operator new(size_t sz)
{
cout<<"Operator new"<<endl;
return malloc(sz);
}
void operator delete(void* p)
{
cout<<"Operator delete"<<endl;
free(p);
}
private:
char* _name;
int _id;
}
int main()
{
return 0;
}

size_t 类型无需担心会不会申请出错

new与delete重构应用

生成栈对象的条件

1.需要合法的构造函数
2.需要合法的析构函数

生成堆对象的条件

1.需要合法的operator new库函数
2.需要合法的构造函数

只能生成栈对象

方法:将 operator new库函数私有化

#include <iostream>
#include <cstdlib>
#include <cstring>

using std::cout;
using std::endl;

class Student
{
public:
Student(const char* name,const int& id)
:_name(new char[strlen(name) + 1]())
,_id(id)
{
strcpy(_name,name);
cout<<"Have done create"<<endl;
}

void release()
{
delete[] _name;
_name = nullptr;
}
~Student()
{
if(_name) release();
cout<<"~Student"<<endl;
}

void print() const
{
cout << "name:" << _name << endl;
cout << "id:" << _id << endl;
}
private:
char* _name;
int _id;
void* operator new(size_t sz){}//因为不需要创建堆对象,所以去重构的过程没有意义,只需要在private内声明即可
void operator delete(void* p){}
};

void test1()
{
Student s1("Rose",100);
s1.print();
}
int main()
{
test1();
return 0;
}

打印结果

Have done create
name:Rose
id:100
~Student

只能生成堆对象

方法:将析构函数私有化

#include <iostream>
#include <cstdlib>
#include <cstring>

using std::cout;
using std::endl;

class Student
{
public:
Student(const char* name,const int& id)
:_name(new char[strlen(name) + 1]())
,_id(id)
{
strcpy(_name,name);
cout<<"Have done create"<<endl;
}

void release()
{
delete[] _name;
_name = nullptr;
}


void print() const
{
cout << "name:" << _name << endl;
cout << "id:" << _id << endl;
}
void* operator new(size_t sz)
{
malloc(sz);
cout<<"Operator new"<<endl;
}
void operator delete(void *p)
{
delete p;
cout<<"Operator delete"<<endl;
}
private:
char* _name;
int _id;
~Student()
{
if(_name) release();
cout<<"~Student"<<endl;
}
};

void test2()
{
Student* sp1 = new Student("Jackie",101);
sp1->print();
sp1->delete;
}
int main()
{
test2();
return 0;
}

此时我们运行发现,在sp1->delete行出错,由此我们知道delete在销毁对象时调用了析构函数,而析构函数因为他的私有化而不可用,因此需要在public里新建一个函数用来替换delete操作

void destroy()
{
this->~Student();
}

此时我们在内存检测时发现,对象本身没有被真正销毁,因此在destroy内部调用析构函数是错误的,应该直接进行delete操作,由于是在类的内部进行delete,可以访问析构函数,所以其操作是完全可行的

void destroy()
{
delete this;
}

代码实现

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

//new 和 delete 都是用operator的标准库函数

class Student {
public:

Student(const char* name, const int& id)
:_name(new char[strlen(name) + 1]())
, _id(id)
{
strcpy(_name, name);
cout << "Have done create!" << endl;
}
void release()
{
delete[] _name;
_name = nullptr;
}

void destroy()
{
//直接调用析构函数不能销毁对象
//this->~Student();
delete this;
}

void print() const
{
cout << "name:" << _name << endl;
cout << "id:" << _id << endl;
}
//这里的new和delete只针对该类的创建销毁,放在全局定义时则会变成对全部的new和delete重构
void* operator new(size_t sz)//不创建就会系统默认提供
{
cout << "Operator new" << endl;
return malloc(sz);
}
void operator delete(void* p)
{
cout << "Operator delete" << endl;
free(p);
}
private:
char* _name;
int _id;

~Student()
{
if (_name) release();

cout << "Have done delete" << endl;
}
};
void test2()
{
//创建堆对象的条件
//需要合法的operator new库函数
//需要合法的构造函数
Student* sp1 = new Student("Jackie", 100);
sp1->print();
sp1->destroy();
//delete sp1; 因为私有化,delete不可用,需要优化

//如何让一个类只能生成栈对象,不能生成堆对象
//解决方案
//将operator new库函数私有化
}
int main()
{
//new的工作步骤
//调用operator new标准库函数申请未定义类型的空间
//在该空间调用构造函数初始化对象
//返回一个相应类型的指针
//形式:void* operator new(size_t)


//delete工作步骤
//调用析构函数
//调用operator delete标准库函数回收对象空间


test2();

return 0;
}

打印结果

Operator new
Have done create!
name:Jackie
id:100
Have done delete
Operator delete