前言
对于自己学习C++编程语言时遇到的一些细节问题做了总结
int pos = xx.find(“,” , 0) ;
find后面要加个默认的查找位置 0
开辟新空间相关( new 数据类型)
开辟元素
数据类型* a = new
数据类型(10);
开辟一级指针数组
数据类型* arr = new 数据类型[10];
父类 * sb = new 子类;
(满足多态的条件下) // 父类指针指向子类地址
开辟二级指针数组
数据类型 **arr = new 数据类型* [size];
// 二级指针指向一级指针的地址
常用添加及存储数据的方式(增)
当数据类型都一样的时候
创建一维数组即可
静态的一维数组 静态存储区域分配 编译的时候确定长度
1. ` 数据类型 数组名[ 数组长度 ]; `
2. `数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};`
3. `数据类型 数组名[ ] = { 值1,值2 ...};`
动态的一维数组 存放在堆区 程序运行的时候确定长度
数据类型* arr = new 数据类型[10];
需要New的时候一般对于复杂类型,这时候内存分配是在堆区。比如自定义的类类型,或者需要大量内存空间的时候使用
当数据类型不一样或自定义类的时候
可以用 Vector / Deque / Map(Multimap) 容器 。
#include <vector>
vector<数据类型> v;
// 遍历容器里面的元素
for(vector<数据类型>::iterator/*也可用auto关键字*/ it = v.begin(); it != v.end(); it++) //传统for循环
{
cout << *it << end;
}
for(i : v) //范围for循环
{
cout << i << endl;
}
或者满足多态条件下的类可以用二级指针:
父类** father = new 父类 * []; //开辟父类指针数组空间
父类* sb = new 子类; //父类指针指向子类地址
father[i] = sb; // 将 子类地址 存放在 父类数组中
文件读写交互
一般来说步骤有 写入文件 、读取文件、输出文件
写入文件
void SpeechManager::saveRecord()
{
ofstream ofs;
ofs.open("speech.csv", ios::out | ios::app); //用追加的方式打开文件 -- 写文件
//将每个人数据写入到文件中
for (······)
{
ofs << ····· << ","
}
ofs << endl;
//关闭文件
ofs.close();
cout << "记录已经保存" << endl;
}
读取文件内容 (一般放入管理类的构造函数里)
- 第一次使用,文件未创建
- 文件存在,但是数据被用户清空
- 文件存在,并且保存职工的所有数据
void SpeechManager::loadRecord()
{
ifstream ifs("speech.csv", ios::in); //输入流对象 读取文件
//1、文件不存在的情况
if (!ifs.is_open())
{
this->fileIsEmpty = true;
cout << "文件不存在!" << endl;
ifs.close();
return;
}
//2、文件为空的情况
char ch;
ifs >> ch;
if (ifs.eof())
{
cout << "文件为空!" << endl;
this->fileIsEmpty = true;
ifs.close();
return;
}
//文件不为空
this->fileIsEmpty = false;
ifs.putback(ch); //读取的单个字符放回去
string data;
int index = 0;
while (ifs >> data)
{
//cout << data << endl;
vector<string>v;
int pos = -1;
int start = 0;
while (true)
{
pos = data.find(",", start); //从0开始查找 ','
if (pos == -1)
{
break; //找不到break返回
}
string tmp = data.substr(start, pos - start); //找到了,进行分割 参数1 起始位置,参数2 截取长度
v.push_back(tmp);
start = pos + 1;
}
this->m_Record.insert(make_pair(index, v));
index++;
}
ifs.close();
}
查看文件内容
- 在speechManager.cpp中实现成员函数
void showRecord();
void SpeechManager::showRecord()
{
if(this->fileIsEmpty)
{
cout << "文件不存在,或记录为空!"<<endl;
}
else
{ ······· }
system("pause");
system("cls");
}
常用删除数据的方式(删)
删除数组中的一个或一组元素
指定删除元素位置之后的元素都往前移。
// index 为指定删除元素 遍历到最后一个元素的-1的
// 让数组前面一个元素赋值为后面一个元素的值 实现元素前移
for (int i = index; i < this->m_EmpNum - 1; i++)
{
this->m_EmpArray[i] = this->m_EmpArray[i + 1];
}
模板相关
首先需要知道的是把模版类的定义和实现分成.h与.cpp写,编译将会出错。
容器遍历几种方式
vector<int> a = { 0,1,2,3,4,5,6,7,8,9 };
for (vector<int>::iterator it = a.begin(); it != a.end(); ++it)
{
cout <<*it << " ";
}
cout << endl;
// 用关键字 auto 可自动识别 it的数据类型
for (auto it = a.begin(); it != a.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//知道容器数量,可用循环下标遍历
for (int i = 0; i < 10; ++i)
{
cout << a[i] << " ";
}
cout << endl;
//可用范围遍历输出数据,但范围for不能遍历容器的增加和删除相关操作。
for (auto r : a)
{
cout << r << " ";
}
char[]取地址
C++ 中,将 char * 或 char[] 传递给 cout 进行输出,结果会是整个字符串,如果想要获得字符串的地址(第一个字符的内存地址),可使用以下方法:
强制转化为其他指针(非 char*)。可以是 void *,int *,float *, double * 等。* 使用 &s[0] 不能输出 **s[0]**(首字符)的地址。因为 &s[0] 将返回 char*,对于 char*(char 指针),cout 会将其作为字符串来处理,向下查找字符并输出直到字符结束 *****。
#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
char var[MAX] = {'a', 'b', 'c'};
char *ptr;
// 指针中的数组地址
ptr = var;
for (int i = 0; i < MAX; i++)
{
cout << "Address of var[" << i << "] = ";
cout << (int *)ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 移动到下一个位置
ptr++;
}
return 0;
}
输出结果:
Address of var[0] = 0x7fffd63b79f9
Value of var[0] = a
Address of var[1] = 0x7fffd63b79fa
Value of var[1] = b
Address of var[2] = 0x7fffd63b79fb
Value of var[2] = c
C++常见的字符串处理函数
#include< string >
- 应用于查找的find()函数
- 子串substr()函数
- 替换replace()函数
- 插入:insert()函数
- 添加字符串:append()函数
- 交换字符串:swap()函数
- 字符串比较函数:compare()
- 字符串大小
#include< string.h >
strcpy(s1,s2)
strcat(s1,s2)
strlen(s1)
strcmp(s1,s2)
strchr(s1,ch)
strstr(s1,s2)
memcpy (void *dest, const void *src, int size)
内存对齐
什么是字节
字节(Byte)是存储数据的基本单位,并且是硬件所能访问的最小单位(解释:硬件是通过地址总线访问内存的,而地址是以字节为单位进行分配的,所以地址总线只能精确到字节)
内存中存储数据的最小单位是“位(Bit)”。
即字节是存储数据的基本单位,位是存储数据的最小单位。
内存里面存放的全是二进制代码,位只能是 0 或者 1, 8位 = 1字节,换算关系如下:
1B=8bit
1KB=1024B
1MB=1024KB
1GB=1024MB
64位编译器 | 32位编译器 |
---|---|
char :1个字节 | char :1个字节 |
char*(即指针变量): 8个字节(64位的寻址空间是2^64, 即64个bit,也就是8个字节。同理32位编译器) | char*(即指针变量): 4个字节 |
short int : 2个字节 | short int : 2个字节 |
int: 4个字节 | int: 4个字节 |
unsigned int : 4个字节 | unsigned int : 4个字节 |
float: 4个字节 | float: 4个字节 |
double: 8个字节 | double: 8个字节 |
long: 8个字节 | long: 4个字节 |
long long: 8个字节 | long long: 8个字节 |
unsigned long: 8个字节 | unsigned long: 4个字节 |
问题引出:
struct A{
char a;
int b;
short c;
};//sizeof(A) 为12字节
struct B{
char a;
short b;
int c;
}; //sizeof(B) 为8字节
上面两个结构体拥有相同的数据成员 char、short 和 int,但由于各个成员按照它们被声明的顺序在内存中顺序存储,所以不同的声明顺序导致了结构体所占空间的不同。
什么是内存对齐
内存对齐:编译器将程序中的每个“数据单元”安排在字的整数倍的地址指向的内存之中
内存对齐的原则
- 1、对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
- 2、在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
进行内存对齐的原因
(1) 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。(访问两次内存,一次获取 “前一部分” 的值,一次获取 “后一部分” 的值.)
(2) 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(3) 空间原因:没有进行内存对齐的结构体或类会浪费一定的空间,当创建对象越多时,消耗的空间越多。
内存对齐的优点
便于在不同的平台之间进行移植,因为有些硬件平台不能够支持任意地址的数据访问,只能在某些地址处取某些特定的数据,否则会抛出异常;
提高内存的访问效率,因为 CPU 在读取内存时,是一块一块的读取。
智能指针
cat.h
#pragma once
#include <string>
#include <iostream>
using namespace std;
class cat
{
public:
//构造函数
cat(string name1):name(name1)
{
cout << "构造函数:" << name << endl;
}
cat() = default;
//析构函数
~cat()
{
cout << "退出构造:" << name << endl;
}
void get_name() const
{
cout<< name << endl;
}
void set_name(const string &tmp)
{
this->name = tmp;
}
private:
string name;
};
int main()
{
//stack调用 栈上创建
cat A("xx");
//结果:
//构造函数xx
//退出构造xx
//heap
cat* me = new cat("金毛犬");
{
cat* me1 = new cat("萨摩耶");
delete me1;
}
delete me;
//结果: 只有手动加入delete关键字后才能完成析构函数
//构造函数金毛犬
//构造函数萨摩耶
//退出构造萨摩耶
//退出构造金毛犬
//smart point 用独占或者共享指针的方式创建
//1.方式1将 raw pointer 赋予 unique_ptr
cat* me = new cat("柯基");
unique_ptr<cat> tmp{ me };
//2. 方式2
unique_ptr<cat> tmp(new cat("柯基");
//3. 方式3 推荐
unique_ptr<cat> tmp = make_unique<cat>("柯基");
shared_ptr<cat> tmp = make_shared<cat>("柯基");
auto tmp = make_unique<cat>("柯基");
auto tmp = make_shared<cat>("柯基");
system("pause");
return 0;
}
如示例所示:
安全的创建智能指针:
完整:
unique_ptr
shared_ptr
省略:
auto tmp = make_unique
auto tmp = make_shared
值传递不可以 Copy ,只能Move
//引用传递 pass by ref
void XX(const unique_ptr<cat> &c)
{
}
//值传递
void XX(unique_ptr<cat> c)
{
}
unique_ptr 通过move值传递后 指针消失。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 351134995@qq.com