C++随记 一

对象:

基本原则:里面有数据受保护,外面有访问数据的操作,不能直接接触到数据。
接收者自己决定做不做某个动作(然是如果他根本没有提供这样的服务呢?要直接去操作他的数据吗?)
Class: 事物归纳总结成一个类别,东西的总类,相似的东西起个名字,是一个概念
Object:东西,实体
在这里插入图片描述

1、理解接口:类比灯座 和灯泡。

2、::两个冒号是做域的解析符,解析符后面的内容属于解析符前面。解析符前面什么也没有就代表后面的东西是全局的。

3、include做的只是简单的文本插入。

4、C/C++文件的编译过程:

  • 首先是预编译,处理#开头的语句,生成 ‘ .i ’文件(在MAC里面是‘ .ii ’)
  • 然后是编译,就是把代码转化成 汇编语言,生成‘ .s ’文件
  • 汇编,把 汇编语言 转化成 机器语言,是二进制的,能被计算机直接识别和执行,生成‘ .o ’文件
  • 链接,把多个‘ .o ’文件链接成一个可执行文件

5、声明可以重复出现,定义肯定就不能了。所以,include的文件里只能有声明,不能有定义。

6、

构造函数:对象创建时就被自动调用
析构函数:对象删除时自动调用,无参数
两个函数都不需要人为写出来,但最好还是自己写一下

7、

在进入一个大括号之前,整个代码块所需要的存储空间就已经被分配好了,但里面创建的对象并没有构造好,程序执行到那里的时候才会调用相应的构造函数。
在这里插入图片描述

8、

当人为提供了有参构造函数时,编译器就不再提供默认构造函数了,此时你定义对象不加参数时,就会报错,你需要添加一个无参构造函数

9、

new要做三件事,一给变量分配内存空间,二如果是new了一个对象,则还要去调用构造函数,三返回变量的地址。
同理,delete先调用析构函数,再删除空间。

10、

普通的C++程序,操作系统会给C++一段内存空间用于new的时候,C++自己去分配内存

11、delete

delete只能删除new申请的空间

12、new完了记得delete,养成好习惯

13、private

private是对类来说的,不是对对象来说的,不同对象之间可以访问彼此的私有变量。
C++只在编译阶段对变量的访问权限(如private)进行检查。

14、friend

friend生命的函数、其他类可以访问类中的私有变量。

15、initialization list初始化列表

这个是真正的初始化,可以初始化任何类型的数据,包括类。他要早于构造函数执行,他们做完了,然后再做构造函数里面的事情。(对于int 和float类型的变量使不使用初始化列表没太大区别,但如果类的成员是一个对象 就会有不同。)

实际上构造函数中执行的操作得叫赋值,而并非初始化。在进行构造函数里的赋值操作时,如果没有先初始化,则会先进行初始化,如果成员变量是一个对象,就会寻找其默认构造函数,如果没有默认构造函数,就会报错。
在这里插入图片描述

16、小数

  • C++编译器看到小数都会默认他是一个double类型,然后如果前面变量类型是float的话,会把双精度转换成单精度,这样会多做一步转换, 所以通常会在float类型的变量后面加一个f,来告知编译器这是一个单精度
  • 默认情况,输出一个小数,最多只能输出6位有效数字


17、指针与const

  • 常量指针(const int * p):指针指向的地址可以改,指针指向的值不可以改
  • 指针常量(int * const p):指针指向的地址不可以改,指针指向的值可以改
  • const int * const p:都不可以改

18、

传入函数的实参可以不是const修饰的类型,然后在函数体中对参数用const修饰
在这里插入图片描述

19、内存空间

在这里插入图片描述

全局区:
全局区的普遍规律:在全局区中,变量和常量之间有一定间隔,虽然他们都在全局区中

#include<iostream>
using namespace std;

int g_a = 0;
int g_b = 0;

const int g_c_a = 0;
const int g_c_b = 0;

int main()
{
	int l_a = 0;
	int l_b = 0;

	static int s_a = 0;
	static int s_b = 0;

	static const int s_c_a = 0;
	static const int s_c_b = 0;

	const int l_c_a = 0;
	const int l_c_b = 0;

	cout << "局部变量 l_a 的地址为:" << (int)&l_a << endl;
	cout << "局部变量 l_b 的地址为:" << (int)&l_b << endl;
	cout << '\n';
	cout << "局部常量 l_c_a 的地址为:" << (int)&l_c_a << endl;
	cout << "局部常量 l_c_b 的地址为:" << (int)&l_c_b << endl;
	cout << '\n';
	cout << "全局变量 g_a 的地址为:" << (int)&g_a << endl;
	cout << "全局变量 g_b 的地址为:" << (int)&g_b << endl;
	cout << '\n';
	cout << "全局常量 g_c_a 的地址为:" << (int)&g_c_a << endl;
	cout << "全局常量 g_c_b 的地址为:" << (int)&g_c_b << endl;
	cout << '\n';
	cout << "静态变量 s_a 的地址为:" << (int)&s_a << endl;
	cout << "静态变量 s_b 的地址为:" << (int)&s_b << endl;
	cout << '\n';
	cout << "静态常量 s_c_a 的地址为:" << (int)&s_c_a << endl;
	cout << "静态常量 s_c_b 的地址为:" << (int)&s_c_b << endl;
	cout << '\n';
	cout << "字符串常量A的地址为:" << (int)&"hello world" << endl;
	cout << "字符串常量B的地址为:" << (int)&"hello XuLei" << endl;
	cout << '\n';
	return 0;
}

在这里插入图片描述
栈区:
对于栈区中的变量,不能返回其地址。

#include<iostream>
using namespace std;

int* test()
{
	int a = 10;
	return &a;
}

int main()
{
	int* p = test();
	cout << *p << endl;
	cout << *p << endl;
	return 0;
}

在这里插入图片描述
第一次能够顺利打印是因为编译器做了保留,但这种保留不会一直持续。所以第二次打出了乱码。

20、引用(int&):与一个对象死死绑定在一起

给变量起别名,不会开辟新的内存空间,仍然是原来的那片内存

  • 引用必须是合法的内存空间(堆区、栈区),不能是常量,不过常量引用可以这样
  • 一旦引用就不能更改(一个别名只能对应一个变量,不能改)
  • 函数中,引用传递可以达到和地址传递相同的效果
  • 如果函数的返回值是引用,那这个函数调用可以作为左值(不过前提是返回的引用不是局部变量的引用)
  • 引用的本质是指针常量
int a = 10;
int& ref = a;// C++发现这是引用,会自动转化为 int* const ref = &a;
ref = 100;   // C++发现是引用,会自动转化为   *ref = 100;
  • 常量引用:用于修饰函数中的形参,防止误操作
#include<iostream>
using namespace std;

void show_value(const int& x)
{   //本函数只用于打印,形参前加上const可以防止里面的误修改操作
	//x = 100;  这里编译器会报错
	cout << x << endl;
}

int main()
{
	//int& ref = 10; 引用需要一块合法的内存空间,因此这行错误
	//前面加上const就可以了,编译器会自行优化代码为:int temp = 10;
	const int& ref = 10;                      //   const int& ref = temp;
	cout << ref << endl;

	int a = 10;
	show_value(a);

	return 0;
}

21、struct和class的唯一区别

struct的默认权限为公共
class默认权限为私有


22、常函数

  • 在小括号的后面加一个 空格 和 const。
  • 常函数里面的任何成员属性都是不能修改的,非要修改的话,需要在声明成员变量的时候前面加上mutable关键字。
  • 常函数的本质是用const修饰this指针,让this指针指向的值也不可以修改(this指针本就是一个指针常量,指向不能改,但指向的值可以改)。

23、常对象

  • 声明对象时前面加上const,常对象的所有属性都不能修改(除非属性声明时加了mutable
  • 常对象只能调用常函数(因为普通成员函数可以修改成员变量)

24、链式编程思想

类似于这种可以往后无限追加的操作:

cout << "A的值为:" << a << "B的值为:" << b << endl;
就像这种可以无限接左移符号

用函数实现这种编程的时候,返回值不能是void,因为如果返回void则无法继续追加,具体返回什么要视情况而定,下面以重载左移运算符为例:

#include<iostream>
#include<string>
using namespace std;

class person
{
public:
	int m_A = 10;
	int m_B = 10;
};

ostream& operator<<(ostream& cout, person& p)
{
	cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
	return cout;// 若这里的返回值是void则只能输出一次
	            // 无法想自实现的cout那样无限追加
}

int main()
{
	person p1;
	cout << p1 << '\n' << "hello world" << endl;
	return 0;
}

在这里插入图片描述

25、负数取余
在这里插入图片描述

26、带符号和无符号

在这里插入图片描述

unsigned char c1 = -1;  //c1的值是255
signed char c2 = 256;   //c2的值是未定义的

在这里插入图片描述
混合使用带符号和无符号类型会出问题:

unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; //输出-84
std::cout << u + i << std::endl; //如果int占32位,输出4294967264

在这里插入图片描述
说明:

  • 如果int占32位,则模为2^32=4294967296
  • -42转化成无符号类型: - 42 + 2^32 = 4294967254
  • 如果int类型的本来就是个正数,则直接计算,不用转化,哪怕算出来是个负数,再转化成无符号类型

在这里插入图片描述

27、初始化 ≠ 赋值

在这里插入图片描述

28、关于用const修饰 变量、指针、引用

首先要学会读懂一个复杂的变量声明。用下图所示方法:
在这里插入图片描述

const int * const p;

现在我们用上图方法分析这条语句。从右往左读:

  • 最先修饰p这个变量的是const,所以p的值本身不能改变。
  • 然后是*,这个*修饰p,表示p是一个指针。至此p是一个指向不能改变的指针,也就是说p这个指针的内存空间里只能存放一个固定的地址(这也意味着 在声明p的时候必须要对其进行初始化)。所以说上面这条语句其实是有语法错误的哈哈哈。
  • 再往左看,是int,表明p是一个指向int型的指针。
  • 再往左又是一个const,这个const修饰int,表示指针p指向的int型变量是个常量,不能改变。注意:这里的“不能改变” 指的是不能通过指针p来改变,我们可以通过不使用p的方法改变p指向的int型变量。

看下面这个例子:
在这里插入图片描述
你看,上面这个例子中,虽然不能通过p修改a,但是我们可以直接用a修改a。

说到底,一个变量能否修改 还要看这个变量本身在声明时有没有加const。因此也有了下面这一条。
在这里插入图片描述


所以在执行赋值操作的时候就要格外小心,就比如你不能进行以下操作:
在这里插入图片描述
这你就报错啦!!!
p是一个普通指针,你既可以修改p的指向,也可以修改p指向的值。但是a是一个常量,是不能改的,权限发生冲突,所以报错。
在这里插入图片描述
这样就是没问题的啦,不管你p怎么const 怎么不能变,都是你p自己一厢情愿。

  • 注:理解复杂的数组声明
    在这里插入图片描述
    当数组声明出现小括号时,先由内向外,再从右向左
    以下内容摘自《C++Primer》第103页。
    在这里插入图片描述

29、getline函数得到的字符串不包含换行符

在这里插入图片描述

30、string类的输入运算符和getline函数分别是如何处理空白字符的

  • 标准库string的输入运算符自动忽略字符串的开头的空白(包括空格符、换行符、制表符等),从第一个真正字符开始读起,直到遇到下一个空白为止
  • getline()则能够读取空白,直到遇到换行符为止,此时换行符也被读取进来,但是并不存储在最后的字符串中

31、

在这里插入图片描述
如果for循环中没有修改c的值则合法,如果修改了则不合法,c的类型是const char &

32、不同的下标运算符

string和vector的下标运算符及其 操作是类内定义的,而数组的下标运算符及其 操作是C++内置的,这两者在使用上是有区别的。
数组的下标可以是负数,而string和vector的下标必须是无符号类型。
在这里插入图片描述

33、vector互换器

vector<int> v1, v2;
v1.swap(v2);

应用:收缩内存。
详情参考本篇博客

34、reserve()预留内存空间

如果vector中要存放的数据量较大,最好利用reserve()成员预留空间。从而减少内存重新分配次数。

35、列表初始化要注意

对于列表初始化:
在这里插入图片描述

int k = { 3.14 };   //所以这样是错的

36、非必须不使用后置递增递减运算符运算符

37、求值顺序

在这里插入图片描述

C++中只有4中运算符明确规定了求值顺序:

  • &&
  • ||
  • ? :
  • ,

求值顺序与 优先级和结合律 无关
在这里插入图片描述
在这里插入图片描述
特别的关于位运算:
在这里插入图片描述
此外,若移位的位数超过结果的位数,则也会产生未定义行为。

练习:
在这里插入图片描述

( a ):(*ptr != 0) && (*ptr++) 判断ptr指针指向的int值是否为0
( b ):判断ival和ival+1两个值是否都非0
( c ):没有规定<=符号左右的求值顺序,产生未定义行为

38、前置后置递增递减运算符优先级 大于 解引用运算符

在这里插入图片描述

(a):合法。先执行++,也就是迭代器前移,并返回前移前的迭代器,然后对其解引用
(b):不合法。先解引用,得到string类型,然后对string类型++,没有任何意义
(c):不合法。先点运算符取empty()成员,迭代器类型没有任何成员。
(d):合法。
(e):不合法。先解引用,得到string,再++,没有意义。
(f):合法。先++,并返回++前的迭代器,再解引用并取其成员。

39、sizeof并不实际计算其运算对象的值

sizeof中解引用一个无效的指针仍然是一种安全的行为,因为指针实际上并未被真正使用。
sizeof不需要真的解引用指针也能知道她所指对象的类型。
所以下面这段代码不会报错:

int* p = NULL;
sizeof p;
sizeof *p;

40、算术类型的隐式转换

int a = 3.14 + 3;

算术类型之间的隐式转换被设计得尽可能避免损失精度。若表达式中既有整形又有浮点型,整形会转化成浮点型,上面例子中3转化成浮点型,然后执行浮点数加法。但最终在初始化时,被初始化的对象类型无法改变,所以浮点型又被转化成整形。

41、显式类型转换

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值