前言
这篇文章的知识点均来自B站的视频内容,视频链接我会放在文章最下方。记录这篇文章的目的是为了不时回头看看已经学过知识点,视频学的时候方便,但并不适合找一些零碎的,模糊的知识点。
提示:内容的提取是依照我自己已有的知识来筛选的,没有找到的话各位只能去看视频找了。
一、C++基础编程?
示例:基础编程包括指针和结构体
1.指针
声明指针 [数据类型] *p = &a; (我在这想多说一嘴,这个[数据类型]要和a的数据类型一样,即等号两边的数据类型要相同)
这说明P指向a的地址,如果想访问a,就需要解引用,即*p,比如cout<< *p << endl;
常量指针:
Const int *p=&a;
*p=20; //错
p=&b; //对
理由: const排前修饰int *指针叫做常量指针,这个指针的指向是可以被更改的,但指针所指的值不可以修改。
指针常量:
Int * const p = &a;
*p=20; 对
p=&b; 错
理由: const在后修饰p叫做指针常量,这个指针的指向不允许更改,但指针所指的值可以修改。
Const指针和常量都修饰:
const Int * const p = &a;
*p=20; 错
p=&b; 错
理由: const修饰既修饰指针又修饰常量,那么这个指针的指向和所指的值都不允许修改。
记忆方法:
第三个应该是不用说了,我说指针常量和常量指针怎么记:实际上,函数在定义参数的时候,有时候会加"&",也就是引用,把这个加在变量前面,就变成了指针常量,也就是只能修改它的值,但不能修改它的地址,也就是不能改变指针的指向,下面一个代码块形象的展示了这个道理。
#include<iostream>
using namespace std;
void printNewValue(int &a){
cout<<"修改前的值:"<<a<<endl;
a = 10;
int b=20;
a = &b;//不注释会报错
cout<<"修改后的值: "<<a<<endl;
}
int main(){
int a=100;
printNewValue(a);
return 0;
}
结构体
结构体,我理解成是用户自定义的变量集合,里面可以自己定义想要的数据类型,灵活多变,我在这介绍结构体的三种创建方式:
struct Stuedent{
string name;
int age;
};
struct Stuedent{
string name;
int age;
}s;
上述两个结构体的创建方式其实区别很小,第二种就是最后一行多了一个"s",这表示声明了一个变量s代表这个结构体,只不过内部的变量还没被赋值。这两种方式的使用方法如下:(struct在主函数中不写也没关系)
int main()
{
struct student s;
s.name=”asda”;
s.age=18;
return 0;
}
/* 这种写法对应第二种结构
int main()
{
s.name=”asda”;
s.age=18;
return 0;
}
*/
/* 这种声明方式也能起作用。
int main()
{
struct student student s={“asda”, 18};
return 0;
}*/
二、C++核心编程
内存分区模型
C++在执行过程中被划分为四个区域:
- 代码区:存放函数体的二进制代码,由操作系统管理。
- 全局区:存放全局变量、静态变量以及常量。
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
- 堆区:由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
程序在编译后,会生成exe可执行程序,未执行该程序之前分为两个区域。
代码区:
- 存放CPU执行的机器指令。
- 代码区可共享,共享的目的是对于频繁被执行的程序,内存中有一份即可。
- 代码区是可读的,这样做的目的是为了防止程序意外修改了它的指令。
全局区:
- 存放全局变量和静态变量。
- 全局区还包含了常量区,字符串常量和其他常量也存放在此。
- 此区域的数据在程序结束后会被操作系统释放。
注意事项:
栈区中,不要返回局部变量的地址,因为函数执行完局部变量就会被释放掉,拿到地址也没用,是一个非法操作。(操作的时候第一次接受返回的地址能接到是编译器做了处理,后面就接不到局部变量的地址了。)
堆区的局部变量可以返回,它是程序员指定操作的,程序(main函数)没有结束之前返回的地址都不会被操作系统清除。
new操作符
C++利用new操作符来开辟堆区的数据。这部分的数据有程序员来开辟,也 由程序员来手动释放,不手动释放的话程序结束后也会被操作系统释放掉。
开辟空间的语法是: new 数据类型。
释放的操作符是 delete, 如果是数组,则是 delete[] arr.
此外需要注意的是,new返回的是对应类型的指针,也就是值的地址,等号左边需要用指针类型。
引用
引用的操作符是&,也就是取到数据的地址。可以用在参数传递过程中,但要注意的是下面这四行代码,第三行是不合法的,如果非要简写,要写成第四行的效果,编译器会自动把你的代码优化成: int temp=10; const int & c = temp;
int a =10;
int& b = a;// 合法
//int& c = 10;// 不合法,因为引用本身需要一个合法的空间
//解决办法
const int& c = 10;
函数重载
重载也就是同一个方法的不同形式,这些形式不外乎是传入了不同类型,数量参数等。但操作的时候要注意函数重载要写在同意作用域下面。下面是几个重载的例子:
void func(){
cout << "重载的无参调用方式" << endl;
}
void func(int a){
cout << "重载的(int)调用方式" << a << endl;
}
void func(double a){
cout << "重载的(double)调用方式" << a <<endl;
}
void func(int a, double b){
cout << "重载的(int, double)调用方式" << a << b << endl;
}
void func(double a, int b){
cout << "重载的(double , int )调用方式" << a << b << endl;
}
// 函数的返回值不可做为重载的条件
// int func(){
// }
类和对象
类的权限有三种:
- 公共权限 public。类内可以访问,类外也可以访问
- 保护权限 protected。 类内可以访问,类外不可以访问,子类可以访问基类(父类)的保护内容
- 私有权限 private。 类内可以访问,类外不可以访问,子类不可以访问基类(父类)的保护内容
结构体和类的默认权限为公共权限。私有成员可以通过类内的方法改修改。
构造函数:对象创建时自动调用构造函数,可以传参。
析构函数:对象销毁前自动调用析构函数,不可传参。
构造函数调用规则
默认情况下,C++编译器至少给一个类添加三个函数(类内没有任何函数实现的时候,这三个也起作用)
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
当用户自己定义了有参构造函数,C++就不提供无参构造了,但会提供拷贝构造函数。
用户如果自己定义拷贝构造函数,C++就不再提供其他的构造函数。
==注意事项:==编译器默认提供的拷贝构造是浅拷贝,当然拷贝的类型在练手一些小代码的时候不需要考虑到(但是在一些特定条件下,你想搞两个一样的类,然后修改其中一个类内部的值,诶,你会发现另外一个类内部的值也发生变化了,这时候就要考虑到是不是出现了浅拷贝)。原理上是浅拷贝仅仅是搞了个新的指针,指向原有数据的地址,然后把这个指针返回给你。现在也就是有两个指针指向同一块内存了,你不管用哪个指针来修改这块内存,内容都会改变。如何写深拷贝视频中有讲。
静态成员函数和成员变量
总结来说就是以下几点:
-静态成员变量属于整个类所有
-静态成员变量的生命期不依赖于任何对象,为程序的生命周期
-可以通过类名直接访问公有静态成员变量
-所有对象共享类的静态成员变量
-可以通过对象名访问公有静态成员变量
-静态成员变量需要在类外单独分配空间
-静态成员变量在程序内部位于全局数据区 (Type className::VarName = value)
我自己也做一些小测试:
#include<iostream>
#include<string>
using namespace std;
// 静态成员函数既不可读,也不可写正常的成员变量,只能操作静态成员变量
// 普通的成员函数可以操作静态成员变量
class TestStatic{
public:
int b=0;
static int a;
static void printValue(){
cout << "静态成员值a为:" << a << endl;
//cout << "静态成员值b为:" << b << endl;
}
static void changeValue(){
a++;
//b++;
}
//void changeValue(){
// a++;
//}
};
int TestStatic::a = 10;
int main(){
TestStatic test;
test.printValue();
test.changeValue();
test.printValue();
cout << "类名直接访问静态变量:" << TestStatic::a << endl;
TestStatic::printValue();
return 0;
}
输出结果如下:
点击链接看更详细的文章