二
C++数组
Array
可以在堆(heap)上创建一个数组
int* another = new int[5]; //其作用域与在栈上创建不同,直到程序把它销毁之前,它都是处于活动状态的,需要用delete关键字来删除
delete[] another;
使用new动态分配最大的原因是生存期,用new来分配的内存,它将一直存在,直到手动删除它。如果你有一个函数返回一个数组,你必须使用一个new关键字来分配它,除非你传入一个数组的地址参数。在堆上创建数组,该数组指针所指的内容为一个地址,这个地址指向数组的第一个元素。所以,应该在栈上创建数组来避免这种情况,因为像这样在内存中跳跃肯定会影响性能
另外,在栈上创建的数组可以用sizeof获得其大小,而在堆上创建的数组无法直接获得其大小,因为它只是一个地址,所以我们需要在创建数组时记录其大小
> C++11中有内置数据结构std::array,相较于原始数组有很多优点,例如边界检查,记录数组大小
``` C++
#include< array >
std::array< int, 5 > another;
C++字符串
字符串本质上是一个字符数组。
const char* name = "Cherno";
//char* name = "Cherno"这样的代码风格在C++11并没有被舍弃,但是不推荐使用。因为这样的代码会导致指针和字符串字面量类型不匹配的问题,可能会引发未定义行为。建议使用std::string或者const char来代替char。
在C++的标准库中有一个名为String的类,实际上有一个类叫BasicString,它是一个模板类,String是BasicString的一个特化版本,模板参数是char,它是一个字符数组的包装器,它提供了很多有用的方法,例如获取字符串长度,连接字符串,查找字符串,替换字符串等等
std::string怎么工作?它只是一个char数组,有一个char数组和一些函数,用来操作这个数组,它的长度是可变的
另一件常见的事是追加字符串,我们想做cherno + hello!不能写为
std::string name = "Cherno" + "hello!";
发生这种情况的原因是 你实际上是想将两个const char的数组相加(双引号里的东西是const char数组,不是真正的字符串),但是这是不可能的,这样实际上是两个指针相加。一个很简单的方法是把它分开成多行
name += "hello!";
这样做是将一个指针,加到了name。name是一个是字符串,你把它加到一个字符串上,+=这个操作在string类被重载了,所以可以这样写。
或者将两个相加的字符数组其中的一个,显式调用string的构造函数
std::string name = std::string("Cherno") + "hello!";
相当于创建了一个字符串,然后附加这个字符数组给它
字符串字面量
双字符串字面量是在双引号之间的一串字符
定义字符串时如果不使用const关键字而直接写类似char* name = "Cherno"
这样的代码,这样的代码风格在C++11并没有被舍弃,但是不推荐使用
。因为这样的代码会导致指针和字符串字面量类型不匹配的问题,可能会引发未定义行为。建议使用std::string或者const char来代替char。
原因是,你在这里所做的是,你取了一个指向那个字符串字面量的内存位置的指针,而字符串字面量是储存在内存的只读部分的
如果确实需要修改它,只需要将类型定义为一个数组,而不是指针
char name[] = "Cherno";
除了char 还有一种类型叫做wchar_t,这就是宽字符。定义时需要在前加上大写L,表示下面的字符串字面值由宽字符组成const wchar_t* name2 = L"Cherno"
C++也引入了一些其他类型,比如char16_t
,需要在前加上u。char32_t
,加上大写的U
const char16_t* name3 = u"Cherno";
const char32_t* name4 = U"Cherno";
基本上,char是一个字节的字符,char16是两个字节的16位的字符,char32是32位,4字节的字符分别对应utf8,utf16,utf32
那么wchar和char16的区别是什么?因为他们似乎都是两个子节点字符
虽然一直说每个字符是两个字节,然而这实际上是由编译器决定的,它可能是一个字节,也可能是两个字节,也可能是四个字节。在实际应用中,通常不是两个就是四个字节。在Windows上是2个字节,在Linux上是4个字节。所以这其实是一个变动的值。
如果你确实要的是2个字节的,就用char16_t。
再讲讲两个字符串的事情。比如字符串附加。在C++14,有个std::string_literals 给出了一些方便的字符函数
上文讲过,如果需要在一个字符串附加一些其他的字符串,不能使用
std::string name0 = "Cherno" + "hello";
因为这些都是字符串字面量,他们实际上是数组或指针。之前的解决方案是用一个构造函数将其包围起来,使其成为一个string对象。然而,因为在C++14的string_literals库中有办法可以让事情变得简单一点。
可以将s加到字符串的末尾。实际上这是一个函数。他是一个操作符函数,返回标准字符串(对象)
我们还可以使用另一种方法来附加字符串字面量——字母R
在字面量前写上R,这意味着忽略转义字符
在实际应用中,如果我们要打印的东西是有很多行的字符串,可以直接像下面写
const char* example = R"(Line1
Line2
Line3
Line4)";
如果不这样,我们就需要用附加字符串的方法或下面的方法
const char* ex = "Line1\n"
"Line2\n"
"Line3\n";
最后,关于字符串字面量的内存以及其如何工作
字符串字面量永远
保存在内存的只读区域(略)
C++中的const
const在改变生成代码方面做不了什么,它有点像类和结构体的可见性,这是一个机制,让代码更加干净,并对开发人员写代码强制特定的规则。const像是你做出的承诺,他承诺某些东西将是不变的。然而,它只是一个你可以绕过的承诺。我们使用const的原因是这个承诺可以简化很多代码
const除了可以声明一个常量,还有其他几种用法
首先是指针,通常对于一个指针,我们可以修改其指向的内容和其地址
但使用了const(将const放在int*前)后,将无法修改指针指向的内容,但仍可以修改指针的指向(即地址)
const int* a =new int;
使用const的第二种方式是将它放在*之后
int* const a = new int;
它的作用恰好相反,我们可以改变指针的指向,但无法把实际的指针本身重新赋值,指向别的东西
注意,下面两种写法的作用是相同的
int const* a = new int;
const int* a = new int;
当然,可以写两个const,这样既不能改变指针指向的内容,也不能改变指针本身
const int* const a = new int;
在类中以及方法中使用const
class Entity
{
private:
int m_X, m_Y;
public:
int GetX() const
{
return m_X;
}
};
在类中方法的参数列表后写上const,这就是const的第三种用法。这意味着这个方法不会修改任何实际的类(即这个方法只能读取数据),所以我们不能修改类成员变量。如果在GetX中尝试m_X = 2则会出错
定义下面的函数
void PrintEntity(const Entity& e)
{
std::cout << e.Get() << std::endl;
}
如果去掉GetX后的const,将不能调用GetX,因为GetX函数已经不能保证它不会写入Entity了。在这里,e是作为const ENtity的引用的,所以不能将e重新赋值。(这与参数为const Entity*类似)
正因如此有时你会看到函数的两个版本例如一个带有const一个不带
int Get() const
{
return m_X;
}
int Get()
{
return m_X;
}
如果你确实想要将方法标记为const,但由于某些原因,又确实需要修改一些变量。在C++中有一个关键词mutable
这个词意味着它是可以被改变的
mutable int var;
这样即使在const方法中也可以对var作出修改
C++中的mutable关键字
mutable有两种不同的用途,其中之一就是上文中的与const一起使用,另一种是用在lambda表达式中。
C++的成员初始化列表
构造函数初始化列表。这是我们在构造函数中初始化类成员(变量)的一种方式。
当我们编写一个类并向该类添加成员时,通常需要用某种方法来初始化这些成员。在C++中,有两种方法可以做到这一点。第一种是在构造函数中初始化它们,第二种是使用成员初始化列表。这两种方法都可以做到这一点,但是成员初始化列表的效率更高,因为它可以避免不必要的构造函数调用。
class Entity
{
private:
std::string m_Name;
int m_Score;
public:
Entity()
:m_Name("Unknown"),m_Score(0) //成员初始化列表
{
}
}
要注意的是,在成员初始化列表中,应该按照声明的顺序写
。因为不管你怎么写,都会按照声明的顺序初始化。如果你打破这个顺序,这就会导致各种各样的依赖性问题。
我们为什么要使用这个?首先这会让代码看起来整洁,使构造函数干净易读。还有一个功能上的区别,在特定类的情况下,如果写如下的代码
class Entity
{
private:
std::string m_Name;
int m_Score;
public:
Entity()
:m_Name("Unknown"),m_Score(0) //成员初始化列表
{
}
}
标签:char,const,int,C++,学习,简记,数组,字符串
From: https://www.cnblogs.com/realyh/p/17406833.html