C++学习笔记(一)

C语言到C++的过渡

c 复制代码
C++手册:https://www.apiref.com/cpp-zh/cpp.html
------------------------------------------------------------
C语言:面向过程,复用性差,维护成本高;
C++:既面向过程,又面向对象,更加模块化,高度复用性,更容易维护;
------------------------------------------------------------------------
Linux中一切皆文件,也是一种面向对象;
-------------------------------------------------------------------------
创建C++文件:xxx.cpp
------------------------------------------
可以使用gcc编译,但是需要链接标准C++库 gcc xxx.cpp -lstdc++(了解,不建议使用)
----------------------------------------------------------------------------------
推荐使用g++编译,例如 g++ xxx.cpp
---------------------------------------------
名字空间:namespace:
--------------------------
namespace 名字标识
{
   声明或定义名字空间的变量、函数、类etc;
}
---------------------------------------------
C语言:scanf/printf		'\n'
C++:cin/cout		endl
-----------------------------------------
名字空间的作用:避免同名变量及函数之间冲突问题;
-----------------------------------------------------
using可以有类似于typedef的用法
----------------------------------------
比如:
typedef int int32;
using int_32 = int;
------------------------------------
名字空间可以进行嵌套:
namespace A
{
	namespace B
	{
		
		int a = 666;
		void show()
		{
			cout << "good good study, day day up!!!" <<endl;
		}

	}

}
---------------------------------------------------------
C++中的域名解析符(域名访问符)"::"
-------------------------------------------------
cout << A::B::a <<endl; //666
A::B::show();  //good good study, day day up!!!
--------------------------------------------------
using关键字用法:
-----------------------
using + namespace +名字标识   //把名字空间的所有标识导入当前域中
using + 名字标识::指定变量或函数  //指定导入变量标识或函数标识到当前域中
using int_32 = int;   //与typedef用法相同,给类型起一个别名
-------------------------------------------------------------------
1、单行注释:

Ctrl + /

或 先按CTRL+K,再按CTRL+U

取消单行注释:

Ctrl + /

或 先按CTRL+U,再按CTRL+K

2、块注释

Alt + Shift + A
3.代码对齐
Ctrl + i
----------------------------------------------


//c_str():生成一个const char*指针,指向以空字符终止的数组。
-----------------------------------------------------------------------------
//这个数组应该是string类内部的数组

//string-->char*
    //c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同
 
    //这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。
-------------------------------------------------------------------------------
string s = "1234";
    c = s.c_str();
    cout<<c<<endl;//1234
    s = "abcde";
    cout<<c<<endl; //abcde
----------------------------------------------------------------
C++字符串:
-----------------------------
#include <cstring>
-------------------------------
 // 复制 str1 到 str3
   strcpy( str3, str1);
   cout << "strcpy( str3, str1) : " << str3 << endl;
 
   // 连接 str1 和 str2
   strcat( str1, str2);
//连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号

   cout << "strcat( str1, str2): " << str1 << endl;

   string str = str1 + str2;

   // 连接后,str1 的总长度
   len = strlen(str1);
   cout << "strlen(str1) : " << len << endl;
---------------------------------------------------------
引用的语法:
--------------------------------
类型& 引用变量 = 变量;

引用变量就是变量的别名,没有开辟新的变量的空间。

所以引用最大使用场景是传参。
-------------------------------------------------------------
const修饰的常引用的好处:

1.可以提高传参效率

2.可以保护实参的安全

3.可以引用常量(语法糖)
------------------------------------------------------------------------------
函数返回值为引用时的特性,可以做为左值使用:

左值:可以取地址的值就是左值。

右值:不可取地址的值就是右值。
------------------------------------------------------------------------------
引用与指针的区别总结:
-------------------------------------------
1:从编译器角度来讲:
	1.1:引用就是一种升级版的指针。
2:从语法形式来讲:
	2.1:引用是引用的是已经存在一块合法的空间。
	(除了返回局部变量引用的情况)
	2.2:引用变量即是引用空间的变量的别名!!!!!!!
	2.3:指针可以是一个野指针,他可以指向任何的地方。
	如果在C++中单纯使用指针,很容易把程序写的不堪一击。
	(内存很容易被写飞!!!!所以要使用引用)
	2.4:指针可以进行无限次的赋值。
	2.5:引用只可以被初始化一次(C++const修饰的变量必须初始化)
-------------------------------------------------------------------------------------
new与malloc的关系:
---------------------------------
new是一个关键字,本质上是一个operator new函数的调用,
在这个函数中其内部调了malloc,
所以new是malloc在C++中的上层接口函数,
同时new在开辟空间失败时,直接返回一个异常。
而malloc是返加的NULL。
同时,delete 就是free在C++中的上层接口的封装。
new是按类型开辟空间的。
而malloc是按字节开辟空间。
--------------------------------------------------------------
使用new与delete 注意事项
----------------------------------------
使用规范:在使用new开辟堆上连续多个空间时,请使用 "delete[ ]" 来进行释放。
当然如果单个类型开辟就可以使用delete来释放。
--------------------------------------------------------
原因:因为new在开辟空间时,如果开辟多个空间时,
会在起始位置多4(32位编系统)或8(64位系统)个字节的这空间来保存构造析构的次数。
---------------------------------------------------------------
对于自定类型来讲,new不仅开辟空间还会构造对象,
而delete不仅释放空间,还在在释放空间析构对象。
-------------------------------------------------------------------
总结malloc 与new之间的关系:
-----------------------------------------
new的底层就是用malloc实现的。

malloc如果申请空间失败返回NULL

new 如果申请空间失败抛出异常 bad_alloc();

new就是按类型开辟空间的,而malloc是按字节为单位开辟空间的。

对自定义类型来讲:
--------------------------
new 不仅开辟空间,还会调用自定类中的构造函数。delete 不仅释放空间,
还会在释放空间前先调用类中析构函数。
---------------------------------------------------------------
结构体大小:
-------------------
1.C++的结构体与结构体中的非静态属性相关,并遵从字节对齐的原则。

2.结构体的成员函数与结构体大小不相关。

3.空的结构体在定义对象时,会占一个字节。
------------------------------------------------------
C++与C中结构体的不同:
----------------------------------------
1.C++中的结构体可以在结构体域中可以直接声明或定义函数。

2.C++中的结构体是有封装性的,默认封装权限是public
----------------------------------------------
取址运算符在C++的拓展:
---------------------------------
main.c
--------------------
void (*pfunc)() = showInfo;
    pfunc();
//C++获取成员函数地址的方式:& + 类域 + :: + 成员函数名。
    void (Stu::*pfunc1)() = &Stu::learning;
-------------------------------------------------------
     Stu stu = {"zhangsa",18};
    (stu.*pfunc1)();

    Stu* p_stu = (Stu*)malloc(sizeof(Stu));
    (p_stu->*pfunc1)();
---------------------------------------
C++和C中const修饰符的一点不同:
-------------------------------------------
//在C中const修饰的变量可以不赋值初值。
const int a;
//在C语言中const修饰的变量称为只读变量。
--------------------------------------------------------
//在C++中当const修饰符修饰的变量,变量保存的是一个常量时,g++编译器会在编译阶段进行优化。
    //我们把保存这个常的const修饰的变量符号称为符号常量。
    //如果不希望出现这种优化请加上volatile关键字。
------------------------------------------------------------
在 C/C++ 编程语言中,volatile 的第二个特性是"不可优化性"。
volatile 会告诉编译器,不要对 volatile 声明的变量进行各种激进的优化
(甚至将变量直接消除),从而保证程序员写在代码中的指令一定会被执行。
------------------------------------------------------------
volatile int nNum;  // 将nNum声明为volatile

nNum = 1;

printf("nNum is: %d", nNum);

--------------------------------------------------------
总结:
--------
1.const修饰符在C++中当修饰一个变量必须进行初始化。
2.const修饰的变量如果是直接保存的一个常量,
那么g++编译器就会在编译阶段对这个变量符号进行展开。所以也称为符号常量。
C++更推荐这种方式来表示常量,而非C中常用#define的方式。
-------------------------------------------
const int mymax = 1024;
---------------------------------
int arr[mymax] = {0};
-------------------------------------------
C++内联函数,g++优化的体现:
---------------------------------------------
内联函数的概念:
------------------------------------------
C++函数如果由关键字inline修饰,

那么此函数将推荐编译器在编译时将其在函数调用处,

在编译阶段直接以类似宏函数的方式进行替换。
--------------------------------------------------------
C中的函数的调用开销:
------------------------------
1.函数参数入栈,
2.跳转到函数的执行地址
3.函数栈帧的开辟  
4.定义一个寄存器保存返回值 
5.函数栈帧回退。
--------------------------------------------------------------
其作用为:内联函数是一种编译器优化的特性。

内联函数的主要目的是为了提高函数调用的效率,减少函数调用的开销。
------------------------------------------------------
#include <iostream>
using namespace std;
#define MAX(x,y) (x > y ? x : y)
inline int mymax(int a, int b)
{
    return a > b ? a : b;
}
//内联函数需要符合的特性:
//1.代码精简,2.没有递归,3.有没有耗时操作 4.没在过深for循环。
//内联函数也是一种编译阶段优化的特性。
//内联函数的代码是什么?最终可执行程序的代码膨胀。
//在C++中使用g++编译编译.cpp代码时,函数相应的自动加上inline。
int main(int argc, char const *argv[])
{
    int a = 100;
    int b = 200;
    //int c = MAX(a,++b);//宏函数常会有逻辑bug,难以解决。所以C++中使用内联函数。
    int c = mymax(a,++b);//201
    cout << c << endl;
    return 0;
}
----------------------------------------------------------------------
函数声明中的参数默认值(缺省参数)
------------------------------------------
函数参数默认值(缺省参数)的赋值默认值规则:
-------------------------------------------------------------
从右往左依次给定默认值,不可跳跃。

这与函数调用时,函数参数入栈顺序相关。
----------------------------------------------------------------------------------------
意义:提供函数调用的一种灵活形式,同时提高代码的可读性与简洁性。
---------------------------------------------------
C++中的函数重载(overload)
--------------------------------------
函数重载的条件:
------------------------
在同一作用域下,同名函数,函数的形参列表不同(形参类型,个数,顺序不同),

则形成重载关系。

函数重载与返回值类型没有任何关系。
----------------------------------------------
需要注意的是,当函数存在多个重载时,应该尽量避免使用具有默认参数的重载函数,

以免产生二义性。

如果必须使用具有默认参数的重载函数,

则应该在函数调用时提供足够的信息,以便编译器可以正确地选择重载函数。
--------------------------------------------------------
函数重载的意义:
------------------------------
函数重载的作用:
--------------------------
重载函数通常用来在同一个作用域内 
用同一个函数名 命名一组功能相似的函数,
这样做减少了函数名的数量,避免了名字空间的污染,
对于程序的可读性有很大的好处,
------------------------------------------
函数重载是一种静态多态:
---------------------------------------------------------
同一函数名根据不同的形参列表,实现不同的逻辑。

面向对象之微观部分之类的组成

c 复制代码
什么是类,什么是对象?
------------------------------------
类:
-----------
具有同种属性与行为的对象可以抽象为一个类

----------------------------
C++ 在 C 语言的基础上增加了面向对象编程,

C++ 支持面向对象程序设计。

类是 C++ 的核心特性,通常被称为用户定义的类型。
----------------------------------------------------------
定义一个类需要使用关键字 class,然后指定类的名称,

类的主体是包含在一对花括号中,

主体包含类的成员变量和成员函数。
---------------------------------------------
对象:
-----------
面向对象的思想就是把一切事物都看成对象,而对象一般都是由属性和方法组成。 
----------------------------------------------------------------
属性属于对象静态的一面,用来形容对象的一些特性。
------------------
方法属于对象动态的一面,行为就是对象的方法
--------------------------------------------------------------
总结:
-------------
类即为一些具有共有属性与行为的抽象。

对象就是类的实例。
-----------------------------------
类的对象的公共数据成员可以使用直接成员访问运算符"."来访问
-----------------------------------------
需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (.) 来直接访问。
--------------------------
类的抽象与封装:
-------------------
类的抽象:
------------
具体对象进行概括和归纳,
提取共同的属性和方法,
形成一个抽象的模板
--------------
类的封装:
--------------
将类的内部实现细节进行封装和隐藏,
提供一个安全的接口来访问和操作对象。
--------------------------
类的封装 = 抽象出对象的共有属性与行为(类) + (类中的)访问权限。
----------------------------
类中的访问权限:
----------------------
C++的类的封装中,有三种访问权限:public、protected和private,它们分别代表不同的含义:

1.public(公有):
------------------------
- 成员声明为public的时候,
可以在类的内部和外部访问。

2.protected(保护):
---------------------------
- 成员声明为protected的时候,
可以在类的内部或继承类内部被访问。

3.private(私有):
--------------------------------
- 成员声明为private的时候,
只能在类的内部被访问。

- 类的外部代码无法直接访问private成员,
包括其他类和派生类的成员函数。

- 通常,类的数据成员往往会设置为private,以隐藏实现细节,
只通过公有成员函数提供接口访问。
----------
好处:
-------------
访问权限的选择有助于实现封装,

即将类的实现细节隐藏在类的内部,

只提供必要的接口供外部代码使用,

这样可以提高代码的可维护性和安全性。
-----------------------------------------------
如果要访问类中的私有属性或方法,
可以在公有权限下设定相应set与get方法:
-----------------------------------------------
class Rect
{
private:
    int _width;
    int _height;
public:
    int getArea()
    {
        return _width * _height;
    }
    void setWidth(int width)
    {
        _width = width;
    }
    void setHeight(int height)
    {
        _height = height;
    }
    int compair(Rect& other)
    {
        return getArea() > other.getArea() ? getArea() : other.getArea();
    }
};

-----------------------------------------------
    Rect r1;
    r1.setWidth(100);
    r1.setHeight(200);
    Rect r2;
    r2.setWidth(200);
    r2.setHeight(300);
    cout << r1.compair(r2) << endl;
----------------------------------------------------

-------------------------------------------------------------
类中静态属性的作用总结:
-----------------------------------
1.static修饰的成员变量必须在类外全局区进行初始化。
(.cpp源文件中的全局区初始化,不要在.h的声明文件中初始化)
----------------------------------------------------------------------------
2.static修饰的成员变量不依赖于某个对象,可以直接使用类名 + 域名访问符的形式,
直接访问(当然也受访问权限的限制)。
-----------------------------------------------
3.static修饰的成员变量不占有类对象的空间,且此静态成员数据只有一分,
被所有类对象共享。
-----------------------------
所以当类中有需要定义一个为这个类而服务,而不是某个对象而服务的属性时,
就可以把它升级static的成员属性。
--------------------------------------------------
所以当需要一个数据对象为整个类服务,而不是某一个对象服务时,
同时又力求不破坏类的封装性,即可以把此成员隐藏在类的内部,
对外不可见,同时又可以为每个本类对象共享,即可以把此属性升级的静态属性。
--------------------------------------------------------

类中的静态成员函数(方法):
-----------------------------------------
类中提供一种不依赖于对象的调用的方法:类中静态成员方法:
-------------------------------------------------------------------
class 类名:
{
    static 返回值 成员函数名()
    {
        //静态成员方法的方法体:不可以访问类中的非静态成员。
        //只可以访问类中的静态属性。
    }
};
--------------------------------------------------------
静态成员函数特性总结:
--------------------------------
当类中成员函数使用static进行修饰时,
那么此成员函数的形参列表中就没有隐藏的this指针,
没有this指针,那么就无法调用类中的非静态属性。

所以static修饰的静态成员函数只能调用类中的静态成员属性,

不能调用类中的非静态成员属性或方法。

static修饰的类中的静态成员函数的调用不依赖于对象的调用,

可以直接使用类域::直接调用(当然也受访问权限的影响。)
------------------------------------------------------------------------
类中的拷贝构造函数与深浅拷贝:
----------------------------------------
编译器所提供的默认拷贝构造原型:
-----------------------------------------
class 类名
{
    public:
        类名(const 类名& other)
        {
            this->本对象属性... = other.其它对象属性...                        
        }
};
-------------------------------------------------------
当类对象有内部指针指向堆区时的浅拷贝问题与深拷贝的改造过程:
-------------------------------------------------------

通常情况下,拷贝构造函数不应该使用explicit修饰,

避免对象无法隐式调用拷贝构造。
------------------------------------------
explicit(显式的)的作用是"禁止单参数构造函数"被用于自动型别转换,
其中比较典型的例子就是容器类型。
在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数。
----------------------------------------------
拷贝构造的调用时机:
------------------------
1.当定义的本对象使用其它已经构建好的对象进行初始化。

2.当函数传参时,如果参数为类类型,那么将自动调用拷贝构造。

3.当函数返回值为类型时,那么将自动调用拷贝构造。
-----------------------------------------------------------------
总结:当函数传参时,或返回值返回时,
为了减少拷贝构造的产生,
推荐使用引用,避免无效传参或拷贝。
--------------------------------------------------
#include <iostream>
using namespace std;
class Stu
{
private:
    string name;
    int age;
    //如果类中有指针属性时:
    int* p;
public:
    //1.当成员函数的形参变量名与类中的属性变量名相同,用于区分。
    Stu(string name, int age)
    {
        this->name = name;
        this->age = age;
        this->p = new int[1024]();
        cout << "Stu的有参构造" << endl;
    }
    //类中默认提供的拷贝构造:
    //拷贝构造是构造函数的一种重载,发生时机:当定义的对象使用另一个已经存在的对象进行初始化,自动调用。
    Stu(const Stu& other)
    {
        this->name = other.name;
        this->age = other.age;
        //升级深拷贝:
        //1.建立一块可以保存原对象堆上数据的空间。
        this->p = new int[1024]();
        //2.把原对象的数据通过memcpy拷贝到新的空间之中。
        memcpy(this->p,other.p,sizeof(int[1024]));
        cout << "Stu的拷贝构造" << endl;
    }
    
    ~Stu()
    {
        if(this->p != nullptr)
        {
            delete []this->p;
            this->p = nullptr;
        }
        cout << "Stu的析构" << endl;
    }

    void showInfo()const
    {
        cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
    }
    //提供公有的get方法:
    int getAge()const
    {
        return this->age;
    }
};

const Stu& compair(const Stu& stu1, const Stu& stu2)
{
    return stu1.getAge() > stu2.getAge() ? stu1 : stu2;
}

int main()
{
    Stu stu1("lisi",25);
    Stu stu2 = stu1;
    cout << &stu2 << endl;
    cout << &stu1 << endl;
    stu2.showInfo();
    Stu stu3("zhangsan",18);
    cout << "-----------------------------" << endl;
    compair(stu1,stu3);
    return 0;
}
------------------------------------------
C++中的运算符重载的语法形式:
----------------------------------------
返回值类型 operator 运算符(形参列表)
{
    //运算符重载的逻辑:书写与运算符功能匹配的逻辑。
}
--------------------------------------------------
运算符重载注意事项:
-------------------------------
1.在全局中不可重载的运算符有:
=,->,[],()不可以在全局作用域进行符号的重载。
(但是可以在类中作为成员函数的运算符重载)
--------------------------------------------------------------
2.不可重载运算符有:
---------------------------
下面是不可重载的运算符列表:

- 1  . (点运算符)不可重载

- 2  :: (域运算符)即类名+域运算符,取成员,不可以重载

- 3  .* (点星运算符,)与->* 运算符,也是不可重载的。

- 4  ?: (三目条件运算符)不可以重载

- 5  sizeof 不可以重载

- 6   #号预处理运算符

- 7. typeid()类型识别运算符。
------------------------------------------
C++中类中默认提供的=号运算符重载的深浅拷贝的问题:
---------------------------------------------------------------------
自增自减运自算符重载:
---------------------------------
前自增运算符语法形式:

返回值 operator++()
{
    //这就是前++运算符的重载
}

后自增运算符语法形式:
返回值 operator++(int)//哑元
{
    //这就是后++运算符的重载
}
---------------------------------------------------
封装自定义一个字符串类型:(实现两个符串对象的+运算符重载 =运算符重载)
------------------------------------------------------------
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
private:
    char* m_data;
public:
    //无参的构造
    MyString()
    {
        this->m_data = new char[1];
        this->m_data[0] = '\0';
    }
    //有参的构造:
    MyString(const char* c_str)
    {
        int len = strlen(c_str);
        this->m_data = new char[len + 1];
        memcpy(this->m_data,c_str,len);
        this->m_data[len] = '\0';
    }
    //拷贝构造:
    MyString(const MyString& other)
    {
        int len = strlen(other.m_data);
        this->m_data = new char[len + 1];
        memcpy(this->m_data,other.m_data,len);
        this->m_data[len] = '\0';
    }
    //析构:
    ~MyString()
    {
        if(this->m_data != nullptr)
        {
            delete [] this->m_data;
            this->m_data = nullptr;
        }
    }
    //MyString的=号运算符重载函数:
    MyString& operator=(const MyString& other)
    {
        if(this == &other)
        {
            return *this;
        }
        if(this->m_data != nullptr)
        {
            delete [] this->m_data;
        }
        int len = strlen(other.m_data);
        this->m_data = new char[len + 1];
        memcpy(this->m_data,other.m_data,len);
        this->m_data[len] = '\0';
        return *this;
    }


    char* getM_data()const
    {
        return this->m_data;
    }


    char operator[](int index)
    {
        return this->m_data[index];
    }


    MyString operator+(const MyString& other)
    {
        int mylen = strlen(this->m_data);
        int otherlen = strlen(other.m_data);


        char* temp = new char[mylen + otherlen + 1];
        memcpy(temp,this->m_data,mylen);
        memcpy(temp + mylen,other.m_data,otherlen);
        temp[mylen + otherlen] = '\0';


        MyString temp_str = temp;
        delete [] temp;
        return temp_str;
    }
};


ostream& operator<<(ostream& mycout,const MyString& str)
{
    //operator<<(mycout,str.getM_data());
    mycout << str.getM_data();
    return mycout;
}


int main()
{


    MyString str = "gao";
    MyString str1 = str;


    MyString str2 = "hello";
    str1 = str2;


    cout << str << endl;
    cout << str + str1 << endl;
    cout << str[0] << endl;


    return 0;
}
相关推荐
L小李要学习9 分钟前
十一、作业
c语言·开发语言·c++
听听听搁浅12 分钟前
数论知识(取模运算)
c++·数论
sealaugh3214 分钟前
spring boot(学习笔记第十二课)
spring boot·笔记·学习
Ddddddd_15814 分钟前
C++ | Leetcode C++题解之第216题组合总和III
c++·leetcode·题解
每天努力进步!16 分钟前
LeetCode热题100刷题8:54. 螺旋矩阵、73. 矩阵置零、48. 旋转图像
c++·算法·leetcode·矩阵
观鉴词recommend24 分钟前
【c++刷题笔记-贪心】day28: 134. 加油站 、 135. 分发糖果 、860.柠檬水找零 、 406.根据身高重建队列
c++·笔记·算法·leetcode
会翻身的嵌入式咸鱼35 分钟前
蓝桥杯开发板STM32G431RBT6高阶HAL库学习FreeRtos——完成第一个小项目点灯
stm32·学习·蓝桥杯
Alice_JC1 小时前
《昇思25天学习打卡营第11天|计算机视觉-ResNet50迁移学习》
深度学习·学习·计算机视觉·迁移学习
玉标1 小时前
ELK日志系统和Filebeat采集器的学习总结
学习·elk
祖国的好青年1 小时前
基于正点原子FreeRTOS学习笔记——时间片调度实验
笔记·学习