C++_restart

Fast again

  • C++快速过一遍,理清C++基本知识框架。画出思维导图 — 10days.

  • STL回顾其基本特性,重点放在会用上边!!通过leetcode来熟练。 — 3days.

  • 数据结构与算法,基本回顾,通过leetcode来训练。 — fast again!

  • 重点先放在动手写代码上!!!先写起来!!!

有趣的问题

C++ 怎样让函数返回数组

函数调用的实现机理

C++

Basic_usage

reference

引用就是对一个变量起别名,二者的地址相同。不占空间,底层实现机制 链接

引用的好处:

一是传值时 引用可以在同等级别解决问题,而指针是传递N级指针,解决N-1级的问题。

二是在子程序的返回值里,如果返回对象超过了该子函数的作用域会失效,运用引用可以将其值保存下来。

newdelete

C语言中的空间申请与释放

int *pi = (int*)malloc(sizeof(int)*10);
free(pi);

C++中 用new delete配对使用

  1. 对于单变量
int *pi = new int(100);  //还可以初始化
delete pi;
  1. 数组
一维数组
int *p = new int[5]{0};	//分配5个int类型的空间  {} 花括号赋值 可写可不写
delete []p;

多维数组
int (*p)[3][4] = new int[2][3][4]{{{0}}}; //三维数组  初始化赋值的时候{0}到一维啊。
delete []p;

指针数组
char **p = new char*[4]; 
inline

主调函数执行时调用子程序块时,会进行保存现场等操作产生一定的时空开销。对于高频、短小的子程序块,可以使用inline内联函数来减小此开销。 内联实际上是将被调用的子程序块直接嵌入到main中相应的执行处。

inline 优点如上,缺点呢就是,每调用一次,就会在相应的执行位置进行函数拷贝,那么编译之后的文件体量会增大。这也体现了计算机处处时间和空间的矛盾性。另外,inline具体怎么做还要看编译器的做法了。

inline 和 #define还有相似之处呢,都是对其进行粘贴。

typecast

强制类型转换

C语言中

int a = (int)9.999;  // a == 9;

C++

float f = static_cast<int>(10)/3; //隐式类型转换  stati_cast<type>(var);

reinterpret_cast<type>(expression);	//重新解释变量类型

const_cast<type>(expression);	//只能作用于指针和引用,去除const属性。

尤其注意:const_cast(expression) 将变量去除const之后,变量是不能更改的 (const类型的数据不可更改)。

const_cast is only safe if you are adding const to an originally non-const variable. Trying to remove the const status from an originally-const object, and then perform the write operation on it will result in undefined behavior.
原生数据是非 const 的, 可以去除其引用的 const 的属性, 若原生数据是 const 的,去除其引用的 const 属性, 执行任何写入操作都是未定义的。

代码列表中写了const_cast<> 的使用场景,感觉C++太严谨了,某个规则不完善,我就再另写一个规则来补充其用法。另外,一切对const变量的修改都是无意义的!即使能改,也是undefined behavior的,结果无意义。

namespace

命名空间,字面意思来理解就是:对变量、函数、类等的命名放在某一个{space}内起作用。用来解决大型软件开发过程中多人协作时命名冲突的问题。

#include<iostream>
using namespace std;	//此处的std怎么理解? standard --> C++标准函数库

std:: 是个名称空间标示符,C++标准库中的函数或者对象都是在命名空间std中定义的,所以标准函数库中的函数或对象都要使用 std 来限定。

using namespace std 告诉编译器我们将要使用空间std中的函数或者对象。标记之后,标准库中的函数就可以直接用了,否则使用时每次都要写 std::cout<<"666"<<endl;

namespace
1. ::  称为域解析操作符,在C++中用来指明要使用的命名空间。
2. 使用 using namespace jmx;

同理,如果想用自己编写的函数库开发,只要将std更换为自己的就可以了。

对于多人协作的命名冲突:尽管相同的变量名,但是使用时分清楚是谁的命名空间就可以解决了。

string class

string 作为C++处理字符串的 类 ,经过重载运算符和添加成员函数,是的C++操作字符串更方便了。

  1. 重载了 + = < > += !=,操作更加便捷。
  2. 为string类添加了成员函数。在程序 string.cpp中有示例。
    • 重点:str.c_str(); 返回的是const char * 类型的 指向字符串的指针常量(临时存在)。简单解释

Class_oop

Encapsulation

封装就是将抽象得到的数据和行为相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成类。其中数据和函数都是类的成员,目的在于将对象的使用者和设计者分开,以提高软件的可维护性和可修改性。

数据:在程序中我自己定义的数据类型,如struct、int等。

行为:即对数据的操作,也就是函数块。

封装呢,就是将数据定义和行为封装在一起。对内数据开放,逻辑抽象,所有东西都是可见的;而对外只提供接口。 --> 实现将对象的设计者和使用者分开。

从C到C++对于封装的演进

C语言中的 struct 只能包含变量,而 C++ 中的 class 除了可以包含变量(数据),还可以包含函数(行为)。C语言中,将函数放在了数据定义的外面,然后把数据以指针的形式传给行为,数据和行为是分离的;

而在 C++ 中,我们将它放在了 class Student 内部,使它和成员变量聚集在一起,更像一个整体。通过类定义出来的变量呢就叫做“对象”! 类只是一个模板(Template),编译后不占用内存空间,所以在定义类时不能对成员变量进行初始化,因为没有地方存储数据。只有在创建对象以后才会给成员变量分配内存。

C++ Class

  1. 权限控制。 public、private、protected
  2. 在class中,将数据和行为放在一起定义
  3. 对内数据开放:不管数据是私有的还是公有的,在class内部的行为都可以使用。
  4. 对外只提供接口来使用。
  5. 使用流程:定义类,生成类对象,对象调用行为来实现需求。

类名其实也是一个namespace。 在函数

Constructor

构造器,在类创建的时候自动调用的(类似于函数的东西,可以用来对类对象的自动初始化。

比如String类,既可以string s; 也可以string s("xxxxxx");就是构造器的作用。

基于封装的栈的练习,可以将栈的 void init()该函数放在构造器中,使其在创建对象的时候自动将空间进行初始化。二是对于栈空间大小,可以使用构造器初始化传参来确定栈大小。

构造器特性

  • 与类的名字相同,类似于函数但没有返回值,生成对象的时候自动调用进行初始化工作。

  • 可以有参数 --> 有参数就会有重载、默认参数的行为。

    重载即为 有参和无参构造两种。在构造时重载和默认参数二者不能同时用,否则会起冲突。

  • 当我们不写构造器的时候,系统会默认生成一个空构造。

Destructor

析构:销毁对象空间。释放掉系统自动分配的栈空间和手动new的堆空间

对象的销毁时期:一是栈对象结束生命周期 {} 时,二是堆对象被手动delete时。

析构器的特性

  • 与类名字相同 ~destructor() 无参数
  • 对象销毁时自动调用,主要是来处理类中自己手动申请的对空间。
  • 析构函数的作用,并不是删除对象,而在对象销毁前完成内存空间的清理,准备做好“善后”工作。

注意一点,析构和delete在C++中是在同级删除空间。

//类定义
Class Stack
{
	Stack()
	{
		space = new char[111];
	}
	~Stack()
	{
		delete[]space;
	}
private:
	char *space;
};

//定义对象
Stack *s = new Stack;
delete s;

比如在上述程序段中,定义对象时自动调用构造和析构,在对象结束周期的时候自动对Stack内的space空间进行销毁。但是在定义对象时也是在堆空间上的,所以后续释放应该再手动添加。作用结果:先释放space的空间,再释放对象s的空间。 (类比C中的free顺序,都是一致的)

Copy contructor

拷贝构造,即由己存在的对象,通过拷贝构造器来创建新对象。其内容都一致。

拷贝构造发生时机:创建对象时依赖已有的对象来赋值; 在调用函数时传对象或者返回对象。

浅拷贝和深拷贝

​ 浅拷贝:A对象对应于一段物理空间,B对象(同类)拷贝A对象来进行构造。如果B对象拷贝完之后没有生成其自己的物理空间,而是指向A对象的空间。此时呢,就是浅拷贝。 这样会导致在析构的时候,A对应的物理空间就会重析构。double free.

​ 深拷贝:类比于浅拷贝,B在拷贝构造的时候,会自己申请内存空间,然后再将A的内容拷贝过来。深拷贝更加安全。

类构造的参数列表赋值法

函数声明之后,实现体之前, 开头 : x(i),

参数列表的初始化顺序,和变量的声明顺序有关,与初始化列表的顺序无关。

class Stu
{
public:
	A(string na)
		:len(strlen(name.c_str)),name(na)	 //参数列表格式 效率更高
	{	
	}
private:
	int len;
    string name;
};

上述顺序是不对滴。

理解参数列表的变量声明顺序,在private中 先定义的len,而后是name。所以在使用的时候不能先用len。。fatal! 关键还是看你定义时变量的顺序。

private:
	string name;
    int len;

先定义name,后边len的时候会用到name。----> 注意参数列表中变量的定义顺序

this pointer

this指针是指向当前对象的指针。

应用1. 成员函数的链式应用。

本质上还是理解this指针指向当前的对象,所有指向的内容都是在当前对象里有效。

class Stu
{
public:
	Stu * add()
	{
		this->age++;
		return this;
	}
private:
	int age;
};

//应用
	Stu s;
	s.add()->add()->add(); 

类对象的练习 P114

Class extension

class member storage

对于类的大小,通过sizeof可以得知仅仅只是类中定义数据变量的大小。那么成员函数定义在哪里呢。

实际上,每个对象都会分配数据成员的空间,但是所有类对象共享一个成员函数。也就是说,数据成员是类对象独有的,而成员函数是大家共有的,都可以调用。这样节省了成员函数的存储空间。

但是新问题又来了,每个对象又是怎么来调用成员函数的呢。

就像主程序里边的函数调用,通过传参来使得函数为你服务。在不同的对象调用成员函数的时候,区分不同对象的标志是对象的this指针。对象调用成员函数的时候,会将其this指针作为隐含的参数传入,为之服务。

const

const修饰类分为三种。 const就是钢铁直男,只要我定义了,你不管怎样都不能修改数据的!

  1. 修饰数据变量

    使用参数列表来进行数据初始化。

  2. 修饰成员函数

     void func() const {}
    
    

    const 置于函数名之后,实现体之前。要求在函数声明和定义处都要声明const。(唯一确定函数,防止重载

    const修饰的函数,不会修改数据成员。只能访问const修饰的函数 -> 防止调用的函数修改数据

    能访问所有类内数据成员,但是不可以修改哦。

  3. 修饰对象

    const string s1;
    
    

    const 修饰函数,要求函数不能修改数据

    consti修饰对象,要求只能调用const函数,const函数来实现不改变数据。 一层一层管控。

static

static为静态的,C++中static实现了在一个类内,多个对象可以共享的一个数据成员。(类似于成员函数,为类所有,不属于具体的对象) 成员函数每个对象都可以传其this指针对其调用,static变量实现同类之间数据的共享,协调行为。

修饰数据成员

  1. 类内声明 static int a; 类外定义int classname::a = 1; 使用时既可以通过类访问classname::a 也可以通过对象访问classmember.a
  2. 静态数据成员属于类,在类外存储。不占用对象空间。

修饰成员函数

  1. 静态成员函数只能访问静态变量。来实现对静态变量的管理。非静态成员函数, 在调用时 this指针时被当作参数传进。 而静态成员函数属于类, 而不属于对象, 没有 this 指针。
static const

二者修饰效果叠加,既不可以改变数据,又能实现数据共享。

初始化数据 const static int a = 100;

修饰成员函数 const static int display() 只能访问const static 修饰的变量。

class_pointer

指向类数据成员的指针, 是类层面的指针, 而不是对象层面的指针。

首先定义一个Stu的类,数据包括 string nameint age;

指向数据成员(public 数据)

string Stu::*ps = &Stu::name; // 定义指向数据变量的类指针

cout<<s1.*ps<<endl;		//栈数据使用指针
cout<<s2->*ps<<endl;	//堆数据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值