C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解

看了这篇博客,将会学到C++的基本操作!(如果不敲代码,可能会一知半解)

12天学好C++ 系列(Day3)


 第三天 - 220929

4.运算符

4.1..点(point)运算符和->指针(pointer)运算符

d=donut.getArea();

- 把函数值(得到的面积值)赋值给d

d=p ->getArea();

- p是一个指针,指向类型为获得面积函数,也可以写为

d=(*p).getArea();

  • 定义指针变量
  • 使用一元运算符 (&) 将变量的地址赋给指针,该运算符返回该变量的地址。
  • 使用一元运算符 (*) 访问存储在地址中的值,该运算符返回位于其操作数指定的地址处的变量的值。

 如图所示:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第1张图片

 我们将数据类型与指针相关联的原因是它知道数据存储在多少字节中。当我们递增指针时,我们按指针所指向的数据类型的大小来增加指针。

#include
using namespace std;

void geeks()
{
	int var = 20;

	//声明指针式变量	
	int* ptr;

	//ptr和var的数据类型必须相同
	ptr = &var;

	// 将一个变量的地址分配给一个指针
	cout << "Value at ptr = " << ptr << "\n";
	cout << "Value at var = " << var << "\n";
	cout << "Value at *ptr = " << *ptr << "\n";
}
//驱动程序
int main()
{
	geeks();
}

 结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第2张图片

 计算面积案例:

#include
using namespace std;

class Circle {//定义一个计算圆周率的类
	int radius;//定义一个半径
public:
	Circle() { radius = 1; }
	Circle(int r) { radius = r; }
	double getArea();

};
double Circle::getArea() {
	return 3.14 * radius * radius;
}
int main()
{
	Circle donut;//定义甜甜圈
	Circle pizza(30);//定义披萨(半径为30)

	cout << donut.getArea() << endl;

	Circle* p;
	p = &donut;
	cout << p->getArea() << endl;
	cout << (*p).getArea() << endl;

	p = &pizza;
	cout << p->getArea() << endl;
	cout << (*p).getArea() << endl
		;
}

结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第3张图片

- 如果没有输入半径(为空),使用了指针还是会计算类

4.2.数组名称作为指针

数组名称包含数组中第一个元素的地址,其作用类似于常量指针。这意味着,存储在数组名称中的地址无法更改。
例如,如果我们有一个名为 val 的数组,则 val 和 &val[0] 可以互换使用。

示例1:

#include
using namespace std;


void geeks()
{
	//声明一个数组
	int val[3] = { 5, 10, 20 };

	//声明指针式变量
	int* ptr;

	//将val[0]的地址分配给ptr
	// 我们可以使用ptr=&val[0];(两者相同)。
	ptr = val;
	cout << "Elements of the array are: ";
	cout << ptr[0] << " " << ptr[1] << " " << ptr[2];
}
//驱动程序
int main()
{
	geeks();
}

 结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第4张图片

 C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第5张图片

 如果指针 ptr 作为参数发送到函数,则可以以类似的方式访问数组 val。

 示例2:

#include
using namespace std;

class Circle {//定义一个计算圆周率的类
	int radius;//定义一个半径
public:
	Circle() { radius = 1; }
	Circle(int r) { radius = r; }
	void setRadius(int r) { radius = r; }
	double getArea();

};
double Circle::getArea() {
	return 3.14 * radius * radius;
}
int main()
{
	Circle circleArray[3];//定义3个的圆周率数组

	circleArray[0].setRadius(10);//一个半径为10的圆周率数组
	circleArray[1].setRadius(20);
	circleArray[2].setRadius(30);

	for (int i = 0;i < 3;i++)
		cout << "Circle" << i << "Area is :" << circleArray[i].getArea() << endl;

	Circle* p;
	p = circleArray;
	for (int i = 0;i < 3;i++){
		cout << "Circle" << i << "Area is :" << p->getArea() << endl;
		p++;
	}

}

结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第6张图片

多次元的数组指针(同理)

#include
using namespace std;

class Circle {//定义一个计算圆周率的类
	int radius;//定义一个半径

public:
	Circle() { radius = 1; }
	Circle(int r) { radius = r; }
	void setRadius(int r) { radius = r; }
	double getArea();

};
double Circle::getArea() {
	return 3.14 * radius * radius;
}
int main()
{
	Circle circle[2][3];//定义2*3个的圆周率数组

	circle[0][0].setRadius(1);
	circle[0][1].setRadius(2);
	circle[0][2].setRadius(3);
	circle[1][0].setRadius(4);
	circle[1][1].setRadius(5);
	circle[1][2].setRadius(6);

	for (int i = 0;i < 2;i++)
		for (int j = 0;j < 3;j++){
			cout << "Circle[" << i << "," << j << "] Area is :" << endl;
			cout << circle[i][j].getArea() << endl;
		}

}

结果:

 C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第7张图片

4.2.总结

  • 递增 ( ++ )
  • 递减 ( — )
  • 可以将整数添加到指针( + 或 += )
  • 可以从指针中减去一个整数 ( – 或 -= )
  • 两个指针之间的差异 (p1-p2)

 4.3.C++中的内存管理

C++ 支持动态内存(即在运行时由程序员手动分配内存或存储空间)使用new和delete运算符分配和释放对象的特性。

要理解这一点,我们必须更深入地了解内存管理的概念。在 C++ 中,任何程序使用的内存架构都分为四个部分:

  • 堆栈:堆栈通常是预先分配的内存。在函数内部声明的变量存储在堆栈中。这种类型的内存分配称为静态内存分配,因为变量的大小是在编译时定义的。每当调用该函数时,都会创建一个新的堆栈帧。
  • 堆:这部分内存用于动态分配内存,即程序运行时或程序执行期间分配内存。(不懂请参考)
  • 数据段:全局变量和静态变量存储在数据段中。它只是存储我们在程序中使用的数据。
  • 代码段:程序的所有执行指令都存放在代码段中。它也称为文本段。

 C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第8张图片

 图为【3】内存

C++中,支持三种基本类型的内存分配,

静态内存分配:在静态和全局变量上,这些类型的变量内存在程序运行时分配一次,并在程序的整个生命周期中持续存在;

自动分配内存:函数参数和局部变量会自动分配内存,这种类型的变量在进入相关块时分配,并在退出块(block)时根据需要,多次释放。

动态内存分配:也就是接下来讲的。

改怎么理解呢?假如我们要定义一个字符串来存储某人的姓名,我们会设置一个长度

char name[25] //如果名字少于这个数,变量没有实际使用,相当与有一段空间是空余的,会浪费大量内存。如果是一个印度人的名字,名字比较长大于这个字符串,那么名字就不会完整,会造成一些问题。大多数通用变量,包括固定数组,都分配在称为堆栈的内存区域中,一般情况下,(IDE)VS默认的堆栈大小为1MB,超过大小会发生堆栈溢出并且操作系统会终止程序。

#include
using namespace std;

int main()
{
	int array[10000000];//分配1百万个整数,可能占了4KB内存
}

 这种情况下,会出现

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第9张图片

 也会出现人为限制或数组溢出问题。为了解决这些问题,就用到了动态内存分配。

动态内存分配:是在程序执行期间向操作系统请求内存的一种方法。该内存不是从程序有限的堆栈内存中分配的,而是从操作系统管理的一个更大的内存池中分配的,称为堆。堆大小一般情况下以千兆字节为单位。

4.3.1.动态分配单个变量 - new

可以想成使用new和delete进行动态内存分配

new

new int;//动态分配一个整数

 也就是从操作系统请求一个整数值的内存。new运算符使用该内存创建一个对象。并返回一个包含已分配内存地址的指针,它将返回值分配给自己的指针变量,以便以后可以访问分配的内存。 

int *ptr=new int

 然后可以通过取消引用下一个指针来访问内存

*ptr=7

 如果没有指针保存刚刚分配的内存地址,则无法访问分配的内存。

4.3.2.动态内存分配是如何工作的?

计算机中有可供应用程序使用的内存,当我们运行程序时,操作系统会将应用程序加载到内存中。应用程序使用的内存被划分为不同的区域,每个区域都有不同的用途。一个区域包含代码,另外的区域用于正常操作(跟踪调用了哪些函数,创建和销毁全局和全局变量等)。

动态分配内存时,需要要求操作系统保留该内存的一部分以供程序使用。如果满足此请求,则将改内存中的地址返回给应用程序。那么应用程序可以随意使用该内存,当应用程序使用完内存后,可以将其返回给操作系统以提供给其他程序。

与静态内存不同,程序本身负责请求和处理动态分配的内存。

4.3.3.初始化动态分配的变量

动态分配变量时,可以通过直接初始化或联合初始化来初始化它。

int *ptr1=new int(5);//注意,小括号,直接初始化
int *ptr2=new int{6};//注意大括号,统一初始化

 4.3.4.删除单个变量 - delete

当用完动态分配的变量时,需要明确告诉c++释放内存以供重用。对于单个变量,是通过delete实现的。

delete ptr;
ptr=0;

 delete操作实际并没有删除任何内容,它只是将指向的内存返回给操作系统。然后操作系统可以将该内存重新分配给其他应用程序。

示例1:

#include 

using namespace std;

class Scaler {
public:
    //Constructor
    Scaler() {
        cout << "Constructor of class called"<<  endl;
    }

    //Destructor
    ~Scaler() {
        cout << "Destructor of class called" << endl;
    }
};

int main() {
    //Allocate memory dynamically to array of objects of type Scaler 
    Scaler* s = new Scaler[5];

    // Delete array which was created dynamically
    delete[] s;

    return 0;
}

 结果为

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第10张图片

在上面的示例中,当您将内存分配给由五个 Scaler 类对象组成的数组时,Scaler 类的构造函数将被调用五次。同样,在删除这些对象的同时,析构函数也会被调用五次。 

示例2:

#include 

using namespace std;


int main() {
    int* p;
    p = new int;
    if (!p) {
        cout << "No memory" ;
        return 0;
    }
    
    *p = 5;
    int n = *p;
    cout << "*p=" << *p << endl;
    cout << "n=" << n << endl;

    delete p;
}

 结果:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第11张图片

 示例3:输入自定义数,求平均值(利用循环)

#include 

using namespace std;


int main() {
    cout << "Input number?";
    int n;
    cin >> n;
    if (n <= 0) return 0;

    int* p = new int[n];
    if (!p) {
        cout << "Input number?";
        return 0;
    }
    for (int i = 0;i < n;i++){
        cout << i + 1 << "First Number?";
    cin >> p[i];
     }
    int sum = 0;
    for (int i = 0;i < n;i++)
        sum += p[i];
    cout << "Average is " << sum / n << endl;

    delete [] p;
}

 结果:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第12张图片

  4.3.6.内存泄漏(memory leak)

当我们使用new关键字分配内存而忘记使用delete()函数或delete[]运算符释放内存时,就会发生 C++ 中的内存泄漏。

c++中的内存泄漏示例:

#include  
using namespace std; 
  
void func_with_mem_leak() 
{ 
    int* ptr = new int(5);  
} 
  
int main() 
{ 
    func_with_mem_leak(); 
    return 0; 
} 

 在示例中,我们的函数func_with_mem_leak(),其中我们声明了一个指针*ptr,它保存了一个动态分配的数组的地址。在这里,我们在没有释放动态分配的数组的情况下终止了函数,这导致了内存泄漏(结果无返回)。如果一个程序有内存泄漏,那么它的内存使用量将会增加,因为所有系统的内存空间都是有限的。因此会产生问题。

那么如何避免内存泄漏?

  • 避免使用原始指针
  • 除非要与较旧的库接口,否则切勿使用原始指针。
  • 使用std::string而不是char*std::string类在内部处理所有内存管理。此外,它快速且优化良好。
  • 与其手动管理内存,不如尝试在适用的情况下使用智能指针。
  • 在 C++ 中避免内存泄漏的最佳方法是在程序级别尽可能少地调用 new/delete。

 处理内存泄漏的示例:

#include 
using namespace std;
int main()
{
    int* array = new int[10];     
    delete[] array;  
    return 0; 
} 

 在例子中,我们声明了一个process()函数,它有一个指针*ptr。指针*ptr保存动态分配的整数数组的地址。在下一行中,我们完美地使用了delete函数释放了动态空间。

没有内存被浪费,因为当我们从函数中出来时,我们正在使用 delete 函数释放内存。

再次描述 - C++ 中使用指针的动态内存分配:

在堆部分分配内存是在运行时发生的,而不是在编译时发生的,因此分配堆内存称为动态内存分配。

int array [10]; //在栈中分配;但要在堆中分配,我们需要使用“新”运算符;

int *ptr=new int(10)//在堆中分配内存。new int(10); 在堆中分配的内存;

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第13张图片

 图来自【4】一旦变量超出范围,堆栈中分配的内存将自动释放,但堆中分配的内存并非如此。我们需要使用 C++ 中的 delete 运算符显式释放内存。对于上面的例子:

delete [] ptr;

ptr=null;

#include
using namespace std;
int main() { 
   int *ptr=new int(10);
   for(int i=0;i<10;++i){
         *(ptr+i)=i;
   }
    for(int i=0;i<10;++i){
      cout<<*(ptr+i)<

4.3.总结:

- 用delete时,一定要用new!

4.4.this指针

 面向对象编程中最常见的问题之一就是:调用类的成员函数时,C++如何找到要调用的对象或实例。所以this作为一个隐藏指针。

如果类数据成员和成员函数数据成员具有相同的名称,并且存在需要在成员函数本身中访问类数据的情况。

定义:指针用于区分同名的类和成员函数的数据成员。

 'this'指针是一个常量指针,它保存当前对象的内存地址。

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第14张图片

 示例:

#include
using namespace std;
class test
{
  int a=1;

  public:
  void show()
  {
    int a=10;

    //gives object address 
    cout<<"Current obj address using this:"<a<

 结果:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第15张图片

4.5.string 类

示例1:

#include
#include
using namespace std;

int main()
{
    string str;
    string address("China ,shandong,...");
    string copyAddress(address);

    char text[] = {'L','O','V','E',' ','C','+','+','\0'};
    string title(text);

    cout << str << endl;
    cout << address << endl;
    cout << copyAddress << endl;
    cout << title << endl;

}

 结果:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第16张图片

 示例2:

#include
#include
using namespace std;

int main()
{
    string names[5];
   
    for (int i = 0;i < 5;i++) {
        cout << "NAME:";
        getline(cin, names[i], '\n');
    }

    string latter = names[0];
    for (int i = 1;i < 5;i++) {
        if( latter < names[i]){
        latter = names[i];
        }
    }

    cout << "End name is :" <

结果:

 C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第17张图片

  示例3:文字的替换

#include
#include
using namespace std;

int main()
{
    string a = "java", b = "c++";
    a = b;

    cout <

 结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第18张图片

   示例4:文字的比较

#include
#include
using namespace std;

int main()
{
    string name = "Kitea";
    string alias = "Kito";

    int res = name.compare(alias);
    if (res == 0) cout << "Same" << endl;
    else if (res < 0)cout << name << "<" << alias << endl;
    else cout << alias << "<" << name << endl;

}

 结果

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第19张图片

- compare()  :compare 是逐字符比较的 从第一位开始 若相同则比较下一字符 若不同 就马上出结果了 。所以按照字符比较,前面kit是一样的,o>e,所以kito>kitea.

4.6.string类常用的函数

append():是向string 的后面追加字符或字符串。

运算符:+  ,   +=:在尾部添加字符。

insert():插入字符。

replace():替换字符。

length()、size():返回字符数量。

capacity():返回重新分配之前的字符容量。

erase():删除字符。

clear():删除全部字符。

substr():返回某个子字符串。

find():查找函数

at() []:存取单一字符。

toupper():功能是把小写字母变成大写字母。

tolower():功能是把大写字母变成小写字母.

isdigit():用于检查其参数是否为十进制数字字符。若参数c为阿拉伯数字0~9,则返回非0值,否则返回0。

isalpha():判断字符ch是否为英文字母,若为英文字母,返回非0(小写字母为2,大写字母为1)。若不是字母,返回0。

#include
#include
using namespace std;

int main()

{
    string e("hello C++");
    cout << e.length() << endl;
    e.append("!!");
    cout << e << endl;
    cout << e.at(6) << endl;
    cout << e.find("C") << endl;
    int n = e.find("+++");
    cout << n << endl;
    e.erase(1, 3);
    cout << e << endl;
    char a = '2';
    char c = 'n';
    char d = 'N';
    cout << "Number Test" << endl;
    cout << isdigit(a) << endl;
    cout << isdigit(c) << endl;
    cout << endl << "Alphabet Test" << endl;
    cout << isalpha(c) << endl;
    cout << isalpha(d) << endl;
    cout << isalpha(a) << endl;
    cout << endl << "Lowercase to uppercase letters:" << endl;
    putchar(toupper(c));
    cout << endl;
    cout << endl << "Uppercase letters to lowercase letters:" << endl;
    putchar(tolower(d));
    cout << endl;
    return 0;
}

 结果:

C++ | 12天学好C++ (Day3)->结构图可视化、代码加通俗理解_第20张图片

 

你可能感兴趣的:(-,C++,-,-,编码理解,-,c++,蓝桥杯,开发语言)