初识C++

1.reference(引用)

1.1:引用的概念
引用就是某一变量或常量的“别名”,对引用进行操作与直接操作变量或常量完全一样。引用本质也是通过指针实现的,但是在使用的过程中弱化了内存地址这一概念,因此除了C++以外的面向对象编程语言都保留了引用,哪怕这些编程语言剔除了指针这一概念。

#include <iostream>
using namespace std;

int main()
{
    int a = 100;
    // b是a的引用
    int& b = a;
    // 对b操作和对a操作完全一样
    cout << &a << " " << &b << endl; // 0x61fe88 0x61fe88
    cout << a << " " << b << endl; // 100 100

    return 0;
}

引用的性质
1.可以改变引用的变量的值,但是不能再次成为其它变量的引用。
2.声明引用时,必须同时对其进行初始化。
3.声明引用时不能为NULL。
4. 声明引用时,初始化的值可以是纯数值,此时需要使用const关键字修饰引用,表示常引用,其值不可变。
5. 可以将变量的引用地址赋值给一个指针,此时指针指向的还是原来的变量。
6. 可以建立指针的引用
7. 可以用const对引用加以限定(常引用),这样的引用不可以改变引用的值,但是可以改变原变量的值。

2.字符串(string)

#include <iostream>

using namespace std;


int main()
{
    string str = "Hello";

    // 取出第二个字符
    char c = str[1];
    cout << c << endl; // e
    c = str.at(1);
    cout << c << endl; // e

    // 当范围越界时,[]直接返回'\0'
    cout << str[100] << endl;
    // 当范围越界时,终止程序运行并输出
    /*
     * terminate called after throwing an instance of 'std::out_of_range'
     * what():  basic_string::at
     */
    cout << str.at(100) << endl;

    cout << "main function end!" << endl;
    return 0;
}

string的遍历支持若干方式:for循环、for-each循环(C++11)、迭代器,本次先使用for循环与for-each循环演示。

#include <iostream>

using namespace std;

int main()
{
    string str = "Hello";

    cout << "---------for---------" << endl;
    for(int i = 0;i<str.size();i++)
    {
        cout << str.at(i) << " ";
    }

    cout << endl << "---------for each---------" << endl;
    for(char i:str)
    {
        cout << i << " ";
    }

    return 0;
}

3.函数

1.内联函数

#include <iostream>

using namespace std;

void test1(); // 先声明

int main()
{
    test1();

    return 0;
}

// 内联函数
inline void test1() // 再定义
{
    cout << "test1" << endl;
}

2.函数重载
C++中允许同一个函数名定义多个函数,这种用法就是函数重载。
重载的函数通过参数来进行区分,即这些函数要求必须参数的个数或类型不同。

3.哑元函数
一个函数的参数只有类型,没有名称,则这个参数被称为“哑元”,这个函数就是哑元函数。
哑元函数的主要用途有:
1. 表达参数列表匹配更加严格。
2. 保持函数的向前兼容性。
3. 区分重载的同名函数。

4.类与对象

  1. 类:类是抽象的概念,规定了同一类对象拥有的属性和行为(成员=属性+行为)。
    对象:对象是具体的,是按照类的概念创建出来的实体。

2.一定先有一个类,再有这个类的对象。
类仅仅表示一个抽象概念,并不是说很多对象聚集在一起就是一个类。
在初学阶段,只有类没有这个类的对象没有意义。

3.两种创建对象的方法
栈内存:
这种对象创建后,在其生命周期结束后(所在的花括号执行完成后),自动被销毁。
堆内存:
堆内存对象的创建需要使用new关键字,并且使用指针来指向此对象。
堆内存的对象需要程序员手动使用delete关键字来销毁,如果没有delete,那么这个对象所在的花括号执行完成后,会持续占用内存且无法回收,这种现象被称为“内存泄漏”。轻微的内存泄漏并不会对程序运行有明显的影响,随着内存泄漏的累积,逐渐会导致程序卡顿,甚至无法正常运行。
因此对于堆内存对象,通常有new就一定有一个对应的delete。
已经delete的对象,有时候还可以正常使用其部分功能,但是请不要这要做!
栈内存对象不支持delete关键字。

5.封装

构造函数
构造函数用来创建一个类的对象,在创建对象时还可以给对象的属性赋予初始值。
构造函数不用写返回值类型,函数名称必须与类名完全一致。
当程序员没有手动编写构造函数时,编译器会自动添加一个没有参数的,函数体为空的构造函数。一旦程序员手动编写了任一一个构造函数,编译器不再自动添加构造函数。
构造函数也支持函数重载和函数参数默认值。

#include <iostream>

using namespace std;


class MobilePhone
{
private:
    string brand;
    string model;
    int weight;
public:
    // 编译器自动添加的构造函数
//    MobilePhone(){}
    // 手动添加一个构造函数,来给属性赋予初始值
    MobilePhone(string b,string m,int w)
    {
        brand = b;
        model = m;
        weight = w;
    }

    // 函数重载
    MobilePhone()
    {
        brand = "山寨";
        model = "8848钛金手机";
        weight = 666;
    }

    // 打印当前的属性值
    void show()
    {
        cout << brand << " " << model << " " << weight << endl;
    }
};

int main()
{
    MobilePhone mp1("苹果","14 Pro",188);
    mp1.show();

构建初始化列表:

class MobilePhone
{
private:
    string brand;
    string model;
    int weight;
public:

    // 构造初始化列表
    MobilePhone(string b,string m,int w)
        :brand(b),model(m),weight(w){}

关于构造初始化列表,需要注意以下几点:
1.构造初始化列表的效率比构造函数函数体中赋值更高。
2.如果成员变量是常成员变量,则不能在构造函数的函数体中赋予初始值,可以通过构造初始化列表赋予初始值。
3.如果影响代码的可读写,可以不使用构造初始化列表。
ps:另外,属性值也可以直接赋值。

拷贝构造函数:
ps:如果程序员不手动编辑拷贝函数,编译器会自动给每个类增加一个默认的拷贝构造函数。
// MobilePhone mp2(mp1);
调用默认的拷贝函数;(MobilePhone类);

浅拷贝:
当成员变量出现指针类型时,默认的拷贝构造函数是基于赋值操作的,因此在拷贝的过程中,会出现指针的拷贝,会造成多个对象的属性指向同一个内存区域的问题,这样的现象不符合面向对象的设计规范。
深拷贝:
默认的拷贝是浅拷贝,当属性出现指针类型,应该手写一个深拷贝构造函数。

#include <iostream>
#include <string.h>

using namespace std;

class Dog
{
private:
    char* name;

public:
    Dog(char* n)
    {
        // 创建堆内存对象
        name = new char[20];
        // 数据复制
        strcpy(name,n);
    }

    Dog(const Dog& d)
    {
        // 创建堆内存对象
        name = new char[20];
        // 数据复制
        strcpy(name,d.name);
    }

    void show_name()
    {
        cout << name << endl;
    }
};


int main()
{
    char c[20] = "WangCai";

    Dog d1(c);
    Dog d2(d1); // 拷贝构造函数

析构函数
在上述深拷贝的示例代码虽然解决了指针作为成员变量拷贝对象的问题,但是在构造函数中开辟的内存区域,没有得到合理的释放,造成了内存泄漏的问题。要解决这个问题需要使用析构函数。

析构函数的基本使用,析构函数是与构造函数完全对立的函数。
构造函数:
对象创建时,手动调用,可以有参数,可以重载,函数名是类名,通常用于对象数据的初始化。
析构函数:
销毁对象时自动调用,不能有参数,不能重载,函数名是~类名,通常用于对象占用资源的释放。

#include <iostream>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string n):name(n){}

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};

int main()
{
    cout << "--------start--------" << endl;

    // 创建一个栈内存对象
    Test t1("A");
    // 创建一个堆内存对象
    Test* t2 = new Test("B");
    delete t2;

    cout << "--------finish--------" << endl;
    return 0;
}

6.作用域限定符

名字空间:
最常见的名字空间是std,也被称为标准名字空间,C++源代码中一些基础性的内容都在这个名字空间下,例如:std::cout、std::endl、std::string等。
程序员也可以自己手动设置名字空间
//namespace my_space{}
使用自定义的名字空间
//useing namespace my_space;

7.类内声明,类外定义

类中成员函数可以在类内声明,类外定义,这样可以把声明和定义分离。

#include <iostream>

using namespace std;

class Demo
{
private:
    string str = "私有成员";
public:
    // 类内声明
    void test();
};

// 类外定义
void Demo::test()
{
    // 此函数属于类内,可以访问私有成员
    cout << str << endl;
}

int main()
{
    Demo d;
    d.test();

    return 0;
}

8.this 指针

this 指针的定义:
this 指针是一种特殊的指针,指向类在外部的对象。
判断this指针的指向,只需要查看this指针所在的函数被哪个对象调用,this指向的就是这个对象。
thiis指针的用法:
1.区分重名的成员变量与局部变量

#include <iostream>

using namespace std;

class Teacher
{
private:
    string name;

public:
    Teacher(string name) // 这种重名情况构造初始化列表也可以区分
    {
        // 区分成员变量与局部变量
        this->name = name;
    }

    string get_name()
    {
        // 编译器自动补上this->
        return name;
    }
};

int main()
{
    Teacher t("孔子");
    cout << t.get_name() << endl;

    return 0;
}

2.*this表示引用整个对象
当函数返回值是当前类的引用时,*this表示的是this指向的对象本身,可以作为返回值,这样的函数支持链式调用。

#include <iostream>

using namespace std;

class Value
{
private:
    int value;

public:
    Value(int value):value(value){}

    // 如果一个类的返回值是当前类的引用,表示该函数支持“链式调用”
    Value& add(int i)
    {
        value += i;
        return *this;
    }

    int get_value()
    {
        return value;
    }
};

int main()
{
    Value v(100);
    // 链式调用
    cout << v.add(6).add(-8).add(200).add(34).get_value() << endl; // 332
    cout << v.get_value() << endl; // 332

    return 0;
}

9.static关键字

1.静态局部变量
普通的局部变量加上static修饰就是静态局部变量。
2.静态成员变量
给普通的成员变量加上static修饰,就是静态成员变量。
静态成员变量往往都需要初始化,通常类内声明,类外初始化。

#include <iostream>

using namespace std;

class Test
{
public:
    // 类内声明静态成员变量
    static int a;
    // 非静态成员变量
    int b = 1;
};

// 类外初始化静态成员变量
int Test::a = 1;

int main()
{
    Test t1;
    Test t2;

    t1.a++;
    t1.b++;

    cout << t2.a << endl; // 2
    cout << t2.b << endl; // 1

    // 查看地址
    cout << &t1.a << " " << &t2.a << endl; // 0x408004 0x408004
    cout << &t1.b << " " << &t2.b << endl; // 0x61fe8c 0x61fe88

    return 0;
}

ps:c++中所有的类中的静态变量(局部和成员)都是这个类的所有对象共用一份,这种静态变量不会伴随对象的销毁而销毁;
静态成员变量在成员的一开始就开辟了,不需要对象就可以调用。

静态成员函数
给普通的成员函数加上static修饰,就是静态成员函数。
静态成员函数可以直接在类内定义,也可以类内声明类外定义,但是如果是后者,static关键字只写在声明处。
静态成员函数与静态成员变量相似,可以通过对象调用,也可以通过类名调用,更推荐后者调用。

10.const关键字

const关键字通常被认为是常量,是运行期常量或运行期只读,即const关键字在编译器并不生效。
const能修饰很多变量或函数。
常成员函数
const修饰成员函数,表示常成员函数,这样的函数特点:
1.可以调用非const的成员变量,但是不能修改其数值;
2.不能调用非const成员函数。
ps: 建议只要类的成员函数不修改属性值就写成常成员函数。
常量对象
const可以修饰对象,表示该对象为常量对象。
常量对象的特点有:
1.常量对象中任何成员变量都不能被修改
2.常量对象不能调用任何非const的成员函数
常成员变量
const修饰成员变量时,表示常成员变量。
常成员变量的特点是:
1.数值在运行期不能被修改
2.数值在编译期可以被修改
3.必须赋予初始值,可以在声明后立刻赋值,也可以使用构造初始化列表赋值。
二者都使用时,可以发现构造初始化列表的赋值生效,其原因是构造初始化列表与声明都是在编译期间确定的,构造初始化列表编译顺序靠后。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值