【C++学习笔记】

Lesson01.c++入门

1.1c++关键字

C++总计63个关键字,C语言32个关键字

随着学习的深入,各个关键字都会使用到。

1.2命名空间

当一个项目代码量过大,多人协作时,难免会出现很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

//guo为命名空间的名字
namespace guo{
//命名空间中可以定义变量/函数/类型
    int rad=10;
    int sad=20;
    int ADD(int a,int b){
        return a+b;
    }

    struct LNode{
        struct LNode* next;
        int val;
    }
//命名空间也可以嵌套定义
    namespace A{
        int a;
        int b;
    }
}

 命名空间使用的三种方式

//1.加命名空间名称及作用域限定符
int main(){
    cout<<guo::rad<<endl;
    return 0;
}

//2.使用using将命名空间中某个成员引入
using guo::rad;
int main(){
    cout<<rad<<endl;
    cout<<guo::sad<<endl;//未引入的成员依旧加命名空间名称及作用域限定符
}

//3.使用using namespace命名空间名称引入
using namespace guo;
int main(){
    cout<<rad<<endl;
    cout<<sad<<endl;
}

1.3c++输入与输出

1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件

以及按命名空间使用方法使用std。

2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<

iostream >头文件中。

3. <<是流插入运算符,>>是流提取运算符。

4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。

C++的输入输出可以自动识别变量类型。

#include <iostream>
using namespace std;
int main(){
    int a;
    double b;
    cin>>a>>b;//可以自动识别类型
    cout<<a<<" "<<b<<endl;//endl进行换行
    return 0;
} 

1.4缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

void Fun(int a=0){
    cout<<a<<endl;
}
int main(){
    Fun();//没有参数时,使用参数的默认值
    Fun(10);//有参数时,使用指定的实参
    return 0;
}

 缺省参数的分类

//全缺省参数
void Fun(int a=1,int b=2,int c=3){
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
}

//半缺省参数
void Fun(int a,int b=2,int c=3){
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
}

int main(){
    Fun();          //1,2,3
    Fun(10);       //10,2,3
    Fun(10,20);    //10,20,3
    Fun(10,20,30); //10,20,30
    return 0;
}

注意:

        1. 半缺省参数必须从右往左依次来给出,不能间隔着给

void Fun(int a=1,int b,int c=3){}  //错误,参数必须从右往左依次给出

void Fun(int a=1,int b=2,int c=3){
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
}

int main(){
    Fun(,,3);   //错误,逗号用来分隔参数
    Fun(1);
    return 0;
}

        2. 缺省参数不能在函数声明和定义中同时出现

        3. 缺省值必须是常量或者全局变量

        4. C语言不支持

1.5函数重载

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题

#include <iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right){
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}
double Add(double left, double right){
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

// 2、参数个数不同
void f(){
    cout << "f()" << endl;
}
void f(int a){
    cout << "f(int a)" << endl;
}

// 3、参数类型顺序不同
void f(int a, char b){
    cout << "f(int a,char b)" << endl;
}
void f(char b, int a){
    cout << "f(char b, int a)" << endl;
}
int main()
{
    Add(10, 20);
    Add(10.1, 20.2);

    f();
    f(10);

    f(10, 'a');
    f('a', 10);

    return 0;
}

1.6引用 

引用相当于给变量取别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共同使用一块空间。

使用方法:类型& 引用变量名(对象名)=引用实体;

void TestRef(){
    int a=10;
    int &re=a;//re相当于a,两者地址相同
}

注意:

        1.引用在定义时必须初始化

        2.一个变量可以有多个引用

        3.引用一旦引用了一个实体,在不能引用其他的实体

        4.引用类型和引用实体是同一种类型

void TestRe(){
    int a=10;
    int b=10;

    int &re;   //错误,必须初始化
    
    int &re=a;
    int &ree=a;//一个变量可以有多个引用

    int &re=b;//错误,引用只能引用一个实体
}

常引用

void test(){
    const int a=10;
    int &ra=a;         //错误的,因为a为只读,b为可读可写的
    const int &ra=a;   //正确的方式

    
    int &b=10;     //错误的,10是常量,不可以更改
    const int &b=10;   //正确的方式


    double d=12.22;
    int &rd=d;             //错误的,类型不同
    const int &rd=d;        //正确的,中间会产生一个临时变量,具有常性
}

const可以赋给非const,但是非const不能赋给const。指针同样适用 

使用场景

1.做参数

//引用做参数
void swap1(int &a,int &b){//引用,不是取地址
    int t=a;
    a=b;
    b=t;
} 

//指针做参数
void swap2(int *a,int *b){
    int t=*a;
    int *a=*b;
    int *b=t;
}

int main(){
    int a=1,b=2;
    swap1(a,b);
    swap2(&a,&b);//这里是取地址
}

 2.做返回值

//做返回值
int test1(int &a){
    a+=1;
    return a;//会产生一个临时数据,将临时数据返回
}

int &test2(int &a){
    a+=1;
    return a;//会少创建一个临时变量,提高效率
}

注意:如果函数返回时,出了函数作用域,如果返回对象还在,则可以使用引用返回,如果已经被系统收回,则必须使用传值返回。

int &Add(int a,int b){
    int c=a+b;
    return c;
}

int main(){
    int &ret=Add(1,2);
    Add(3,4);
    cout<<ret<<ednl;   //这里会返回7而不是3,有可能是随机值
    return 0;
}

解决方法:在函数使用static int c=a+b,这样c在静态区,出了函数这块空间不会被系统所回收

引用与指针的区别

  1. 引用在定义时要初始化,指针没有要求
  2. 引用在引用一个实体后不能再引用其他实体,而指针在任何时候都可以指向任何同一类型的实体
  3. 引用在概念上是变量的别名,而指针是存储变量的地址
  4. 没有NULL引用,但是有NULL指针
  5. 引用自加是实体加一,指针自加是向后偏移一个类型大小

1.7内联函数 

c++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,提高系统效率

使用一种以空间换时间的思想,缺陷:会使目标文件变大,优势:少了栈帧的开销,提高系统效率

使用方法:在函数前面加上inline,对编译器而言只是建议,编译器未必会采用,一般小函数会成为内联函数

宏的优缺点:

缺点:不方便调试,代码可读性差,没有类型安全检查

1.8C++11新增

1.auto关键字

void test(){
    int a=10;
    auto b=a;   //会自动进行类型匹配
    int *p=&a;
    auto b1=p;
}

注意:

使用auto定义变量必须对其进行初始化,auto不是类型声明,而是一个类型声明时的占位符

auto不能作为函数参数,不能直接来声明数组

2.范围for循环

void test(){
    int arr[]={1,2,3,4,5,6,7};
    for(auto e:arr){   //相当于e来遍历整个数组
        cout<<e<<endl;
    }
}

条件:

for循环迭代的范围是确定的

3.指针空值nullptr

在C++中建议将空指针置为nullptr,因为C++中将NULL设为0

Lesson02.类和对象

2.1类的定义

class className{
    //类体:由成员函数和成员变量组成
};
//注意后面需要加;

 class为定义类的关键字,className为类的名字,{}中为类的主体

2.2访问限定符

public(公有),private(私有),protected(保护)

public修饰的成员在类外可以直接访问,private和protected在类外不可以直接访问所修饰的成员

class默认访问限定符为private,struct默认访问限定符为public

#include <iostream>
using namespace std;
class Person{
public:
    void printPerson(){
        cout<<"name:"<<_name<<endl;
        cout<<"age"<<_age<<endl;
private:
    int _age;
    char _name[20];
};

//printPerson成员函数也可以在类外定义,当一个项目很大,多人协作时,推荐类外定义

void Person::printPerson()   //指定成员函数是哪一个类
{
    cout<<"name:"<<_name<<endl;
    cout<<"age"<<_age<<endl;
}

2.3类的实例化

用类类型创建对象的过程称为实例化

int main(){
    Person p1;
    p1.printPerson();
    //一个类可以实例化出多个对象
    Person p2;
    p2.printPerson();
    return 0;
}

 2.4类对象的大小

//该类实例出对象的大小为4
class A{
public:
    void print_A(){
        cout<<_A<<endl;
    }

private:
    int _A;
}


//该类实例出对象的大小为8
class B{
public:
    void print_B(){
        cout<<_B<<endl;
    }

private:
    int _B;
    char _cB;
}



//该类实例出对象的大小为1
class C{
public:
    void print_C(){
        cout<<_C<<endl;
    }
}

没有成员变量的类大小为1,为了占位,表示对象存在

只计算成员变量,不包括成员函数,因为一个类实例化出N个对象时,每个对象的成员变量都可以存储不同的值,而调用的函数只有一个

成员变量大小同struct大小的计算方法,内存对齐原则

2.5this指针

class Date
{
public:
//void Init(Date* this,int year, int month, int day)
    void Init(int year, int month, int day){ 
        _year = year;
        _month = month;
        _day = day;
    }

//void Print(Date* this)
    void Print(){   
        cout <<_year<< "-" <<_month << "-"<< _day <<endl;
    }

private:
    int _year; // 年
    int _month; // 月
    int _day; // 日
};

int main()
{
Date d1, d2;
d1.Init(2022,1,11);  //d1.Init(&d1,2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();   //d1.Print(&d1);
d2.Print();   //d2.Print(&d2);
return 0;
}

C++编译器会给每个“非静态的成员函数”增加一个隐藏的指针参数,让该指针指向当前对象

void Display()         //void Display(Date* this)
{
    cout<<_year<<endl;   //cout<<this->_year<<endl;
}

 this指针存在栈,参数由编译器自动添加,不可手动写入

2.6 类的默认成员函数

2.6.1 构造函数

        构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

        构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

其特征如下:

1. 函数名与类名相同。2. 无返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦

用户显式定义编译器将不再生成。

6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数

class Date
{
public:
// 1.无参构造函数
    Date()
    {}
// 2.带参构造函数
    Date(int year, int month, int day)
    {
    _year = year;
    _month = month;
    _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};
void TestDate()
{
    Date d1; // 调用无参构造函数
    Date d2(2015, 1, 1); // 调用带参的构造函数
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
    Date d3();// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
}
2.6.2析构函数 

对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
析构函数是特殊的成员函数,其特征如下:

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

5.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏。

class Time
{
public:
    ~Time()
    {
        cout << "~Time()" << endl;
    }
private:
    int _hour;
    int _minute;
    int _second;
};

class Date
{
private:
    // 基本类型(内置类型)
    int _year = 1970;
    int _month = 1;
    int _day = 1;
    // 自定义类型
    Time _t;
};
int main()
{
    Date d;
    return 0;
}
// 程序运行结束后输出:~Time()

 /*在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
 因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,_day三个是内置类型
成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;
 而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
 但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date
类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部
调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
*/
// 注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
2.6.3拷贝构造函数 

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

特征:

1.拷贝构造函数是构造函数的一种重载。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为     会引发无穷递归调用

3.若未显式定义,编译器会生成默认的拷贝构造函数。

4.拷贝构造函数典型调用场景:使用已存在对象创建新对象,函数参数类型为类类型对象,函数返     回值类型为类类型对象

class Date
{
public:
    Date(int year, int minute, int day)
    {
        cout << "Date(int,int,int):" << this << endl;
    }

    Date(const Date& d)//拷贝构造函数
    {
        cout << "Date(const Date& d):" << this << endl;
    }

    ~Date()
    {
        cout << "~Date():" << this << endl;
    }
private:
    int _year;
    int _month;
    int _day;
    };
    Date Test(Date d)
    {
        Date temp(d);//调用拷贝构造
        return temp;
    }
int main()
{
    Date d1(2022,1,13);
    Test(d1);
    return 0;
}
2.6.4赋值运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表)注意:

不能通过连接其他符号来创建新的操作符:比如operator@

重载操作符必须有一个类类型参数

用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // bool operator==(Date* this, const Date& d2)
    // 这里需要注意的是,左操作数是this,指向调用函数的对象
    bool operator==(const Date& d2)
    {
        return _year == d2._year&& _month == d2._month&& _day == d2._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

赋值运算符重载格式:

        参数类型:const T&,传递引用可以提高传参效率

        返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

        检测是否自己给自己赋值

        返回*this :要复合连续赋值的含义

class Time
{
public:
    Time()
    {
        _hour = 1;
        _minute = 1;
        _second = 1;
    }
    Time& operator=(const Time& t)
    {
        if (this != &t)
        {
            _hour = t._hour;
            _minute = t._minute;
            _second = t._second;
        }
        return *this;
    }
private:
    int _hour;
    int _minute;
    int _second;
};
 2.6.5取地址及const取地址操作符重载

 这两个默认成员函数一般不用重新定义 ,编译器默认会生成

class Date
{
public :
    Date* operator&()
    {
        return this ;
    }
    const Date* operator&()const
    {
        return this ;
    }
private :
    int _year ; // 年
    int _month ; // 月
    int _day ; // 日
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值