C++基础语法4( 类型转换、封装、构造、析构函数、this指针)

目录

类型转换:

1.static_cast(expr):

2.reinterpret_cast(expr);

3.dynamic_cast(expr);

4..const_cast(expr);

封装:

构造函数:

析构函数:

this指针:

初始化列表:


类型转换:

C语言的隐式转换和显示转换举例如下:

#include <stdio.h>

int main()
{
    int num = 5;
    double b = (double)num; //显示转换 强制类型转换
}

C++的类型转换运算符(共四类):

能区分四个类型的作用以及使用方法等

static_cast<T>(expr);
const_cast<T>(expr);
dynamic_cast<T>(expr);
reinterpret_cast<T>(expr);

1.static_cast(expr):

static_cast:相关类型,子类转父类,void*转换成任意类型指针

void* 为万能指针,可以接收任意类型的指针值

#include <iostream>

using namespace std;

int main()
{
    int num = 5;
    
    double b = static_cast<double>(num);
    char ch = static_cast<char>(num);
    
    cout << b << endl;
    cout << ch << endl;
    
    int *p_num = &num;
    char *p_ch = &ch;
    //指针必须是相同类型指针赋值,不同类型指针赋值会导致指针的步长发生变化
    //步长发生变化,会影响到数据的组成
    p_ch = p_num; //无法赋值
    
    int a = 0X12345678;
    int *p =&a;
    char *ptr = p;
    //此时ptr的值小端时为78,大端为12
    
    void *p = p_num;
    void *p2 = p_ch;
    
    int *p_num2 = static_cast<int *>(p);
    //p转换成int形指针
    
    *p_ch = static_cast<int *>(p); //不允许任意类型指针之间转换
    return 0;
}

2.reinterpret_cast(expr);

相当于C语言的强制类型转换(可以转换成任意类型(包括指针))

缺点:不安全,限定适用范围:指针和引用

不建议使用

#include <iostream>

using namespace std;

int main()
{
    int num = 0x12345678;

    
    int *p_num = &num;
    char *p_ch = &ch;
    void *p = p_num;
    void *p2 = p_ch;

    p_ch = reinterpret_cast<char*>(p_num);
    char ch2 = reinterpret_Cast<char>(num);//不支持
}

3.dynamic_cast(expr);

多态 RTTI

4..const_cast(expr);

去除指针和引用的const属性,只能在指针和引用前加(使用较少)

#include <iostream>

using namespace std;

int main()
{
    int num = 0x12345678;

    const int count = 5; //const:常量
    const int *p_count = &count;
    const int &l_rcount = count;
    
    int *p_count1 = const_cast<int*>(p_count);
    //去除了p_count的const属性
    (*p_count1)++;
    cout << *p_count1 << endl; //输出6
    cout << count << endl; //输出5
    
    int &l_rcount = l_rcount; //报错,左右两边必须为const
    int &l_rcount = const_cast<int&>(l_rcount); 
    
    string s("helllo world!");
    char *ptr = const_cast(char *)(s.c_str()); //无法转化常量
    
    return 0;
}

封装:

可以达到对外提供接口,屏蔽数据,对内开放数据。

提高了代码的维护性,继承提高代码的复用性;封装+继承=扩展性

维护性:当代码出现问题时,能很快定位到出现问题的位置;越独立的代码越容易维护

C语言中的封装:当单一变量无法完成描述需求的时候,结构体类型解决了这一问题

问题:即知其接口,又可以直接访问其内部数据。

C++的封装:class封装的本质,在于将数据和行为绑定在一起然后通过对象来完成操作。

权限修饰符:public,private,protected:

class 类名称
{
    public:
        公有成员(外部接口)
    private:
    protected:
        
       
}

编程实例:

1.栈的实现:

C语言:

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

enum Ret
{
    IS_FULL = -199 ,//随意给,不要重复
    IS_NOT_FULL,
    IS_EMPTY,
    IS_NOT_EMPTY,
    PUSH_SUCCESS,
    PUSH_FAILED,
    POP_SUCCESS,
    POP_FAILED
};

int is_full(int top)
{
    if(top == MAX_SIZE-1)
    {
        return IS_FULL;
    }
    return IS_NOT_FULL;
}

int is_empty(int top)
{
    if(top == -1)
    {
        return IS_EMPTY;
    }
    return IS_NOT_EMPTY;
}

void init_stack(int *top)
{
    *top = -1;
}

int push(int * stack, int * top, int* num) //压栈函数
{
    if(is_full(*top) === IS_FULL);
    {
        return POUSH_FAILED;
    }
    (*top)++;
    stack[*top] == num;
    
    return PUSH_SUCCESS;
}

int pop(int * stack, int *top) //出栈函数
{
    if(is_empty(*top) === IS_EMPTY);
    {
        return POP_FAILED;
    }
    return stack[stack(*top)--];
}


int main()
{
    int stack[MAX_SIZE] = {0}; //初始化
    int top;
    
    init_stack(&top);
    
    for(int i = 0; i < 5; i++) //压栈
    {
        if(push(stack,&top,i) == IS_FULL)
        {
            printf("stack is full!\n");
            exit(1)
        }
    }
    
    int temp;
    for(int i = 0; i < 5; i++) //压栈
    {
        if((temp ==pop(stack,&top) == IS_EMPTY)
        {
            printf("stack is empty!\n");
        }
        printf("pop:%d\n",temp);
    }
    return 0;
}

C++

stack.h

#pragma once

class Stack //类名
{
    public: //类的方法,类的成员的方法
        void initStack(int len);
        int push(int num);
        int pop();
        bool isEmpty();
        bool isFull();
        int getTop();
        void freeStack();
    private: //属性,类的成员,私有变量无法改变,安全性高
        int top;
        int *stack;
        int maxLen;
};

stack.cpp

#include <iostream>
#include"stack.h"

void Stack::initStack(int len)
{
    stack = new int[len];
    top = -1;
    maxLen = len;
}

bool Stack::isFull()
{
    if(top == maxLen-1)
    {
        return true;
    }

    return false;
}

bool Stack::isEmpty()
{
    if(top == -1)
    {
        return true;
    }

    return false;
}

int Stack::push(int num)
{
    if(isFull() == true)
    {
        return false;
    }

    stack[++top] = num;

    return true;
}

int Stack::pop()
{
    if(isEmpty() == true)
    {
        return false;
    }

    return stack[top--];
}

int Stack::getTop()
{
    return stack[top];
}

void Stack::freeStack()
{
    delete [] stack;
    stack = nullptr;
}

main.cpp:

#include<iostream>
#include"stack.h"

using namespace std;

int main()
{
    Stack s;
    s.initStack(100);

    for(int i = 0; i< 5; i++)
    {
        if(s.push(i) == false)
        {
            cout <<"stack is full!" << endl;
        }
    }

    for(int i = 0; i < 5; i++)
    {
        cout << s.pop() << endl;
    }
    
    s.freeStack();
    return 0;
}

构造函数:

空类内自带的构造函数:

Empty(const Emply&); //默认拷贝构造函数
~Empty();            //默认析构函数
Empty&operator=(const Empty&) //默认赋值运算符
Empty *operator();   //取址运算符
const Empty* operator&() const; //取址运算符 const

人为操作的时候会忘记初始化和释放内存,C++使用构造函数和析构函数

构造函数:初始化

在编写时,先写构造,再在最后写析构函数,然后在中间添加自定义函数

构造函数:

特点:

1.自动调用(实例对象(定义对象)时,自动调用构造函数)

2.构造函数的函数名与类名一致

3.构造函数没有返回值

4.构造函数可以重载

5.当类里无任何构造函数时,系统会默认生成一个无参的构造函数;有任何一个构造函数都不会默认生成。

补充:C++ delete default

6.构造函数的种类

无参构造函数(系统默认生成的(空的),自定义实现的);

有参构造函数(类型转换构造函数,拷贝构造函数(系统默认生成的,自定义实现的),移动拷贝构造函数)

类型转换构造函数(构造函数只有一个参数!)

explicit:防止编译器产生隐式转换,只能通过构造函数的方式来调用,类型转换构造函数必须加

拷贝构造函数 形参是该类型的对象引用,若类无自定义拷贝构造函数,系统会默认生成拷贝构造函数。

拷贝构造函数什么时候使用?

用已有的对象初始化对象会调用;当函数的形参是对对象的时候;当函数的返回值是类对象,函数执行返回的时候会调用拷贝构造函数。

深拷贝和浅拷贝(默认生成的拷贝为浅拷贝),类内有指针成员才会出现,浅拷贝:多个成员指向同一块空间,深拷贝:多个成员指向不同的空间

移动拷贝构造函数: 解决临时对象拷贝的开销问题,提高运行效率

对象移动:将该对象移动到目标对象的空间(把归属权交给新的对象,不发生拷贝),解决对象拷贝赋值开销的问题

右值引用真正目的是把拷贝对象变成移动对象,省去拷贝构造时间,提高运行效率

如何不生成系统默认生成的无参构造?

自己写构造函数,

Student() = delete;//让系统不生成默认构造

Student() = default; //使用系统默认生成的无参构造。

运算符重载:

Test t8 = t1; //初始化,调用拷贝构造函数
Test t9;
t9 = t1; //隐式调用,赋值: = 运算符 ,等号运算符为浅拷贝
//运用重载运算符解决
Test & operator = (const Test &other)
{
    cout << "operator =" << endl;
    int len = strlen(other.m_ptr);
    this -> m_ptr = new char[len+1];
    strcpy(m_ptr, other.m_ptr);
    
    return *this;
}

//移动拷贝运算符重载
Test & operator = (const Test &other)noexcept //移动拷贝函数需要加
{
    cout << " move copy operator =" << endl;
    this->m_ptr = other,m_ptr;
    other.m_ptr = nullptr;
}

bool operator > (const Test &t)
{
    if(strcmp(t.m_ptr,this->m_ptr) > 0)
    {
        return true;
    }
    return false;
}

t2.operator = t1;//深拷贝

.h文件(添加在C++栈的响应文件中)

#pragma once

class Stack
{
    public:
        Stack(int len = 100); //在类内指定了默认参数 .定义中就不需要指定
        //使用默认参数就无法使用重载
        
        Stack() = delete; //告诉编译器不生成默认无参构造函数
        Stack() = default; //使用系统自动生成的无参构造函数,不用自己构造的函数
        //系统生成的无参构造函数,什么工作都不做,空函数。
        //需要无参函数带有功能则要自己实现
};

 .cpp文件(添加在C++栈的响应文件中)

Stack::Stack()
{
    stack = new int[10];
    maxLen = len;
    top = -1;
}


Stack::Stack(int len)
{
    stack = new int[len];
    maxLen = len;
    top = -1;
}

各类构造函数:以下各类构造函数都建议写入 

#include<iostream>

using namespace std;

class Test
{
public:
    int m_num;
    int m_count;
    string m_s;
    char* m_ptr;
    //无参构造函数
    Test() : m_num(0)
    {
        cout << "Text" << endl;
    }
    //有参构造函数
    Test(int num, int count) : m_num(num), m_count(count)
    {
        cout << "Text(int,int)" << endl;
    }
    //析构函数
    ~Test()
    {
        if (this->m_ptr != nullptr)
        {
            delete[] m_ptr;
            cout << "~Test" << endl;
        }
    }
    //自定义拷贝构造函数(深拷贝,不在同一个地址)
    Test(Test& other)
    {
        cout << "copy Test" << endl;
        int len = strlen(other.m_ptr);
        this->m_ptr = new char[len + 1];
        strcpy(m_ptr, other.m_ptr);   //无置空操作形参加CONST           
    }

    //拷贝运算符重载(浅拷贝,两个指向同一个地址)
    Test& operator = (const Test& other) 
    {
        cout << " move copy operator =" << endl;
        this->m_ptr = other.m_ptr;
        this->m_count = other.m_count;
        this->m_num = other.m_num;
        this->m_s = other.m_s;
    }

    //运用重载运算符解决
    Test& operator = (const Test& other)
    {
        cout << "operator =" << endl;
        int len = strlen(other.m_ptr);
        this->m_ptr = new char[len + 1];
        strcpy(m_ptr, other.m_ptr);
        return *this;
    }


    //移动拷贝构造函数
    Test(Test&& other)noexcept //移动拷贝函数需要加,形参右值引用
    {
        cout << "move copy test" << endl;
        this->m_ptr = other.m_ptr;//对象移动,内存使用权交互
        other.m_ptr = nullptr;
        
    }

    Test getTest()
    {
        Test t(1,2); //临时对象,就是右值
        return t;
    }

    //移动拷贝运算符重载
    Test& operator = (Test&& other)noexcept //移动拷贝函数需要加
    {
        cout << " move copy operator =" << endl;
        this->m_ptr = other.m_ptr;
        other.m_ptr = nullptr;
    }
        
};

int main()
{
    Test t1;
    Test t2(1,2);
    Test t3 = 1; //隐式转换:将整型转为了Test类型
    //添加了explicit就会报错,不加不报错

    string s("hello world!");
    Test t4 = s; //隐式类型转换----危险,添加了explicit就会报错

    //正确做法:
    Test t5(s);
    Test t6(1);
    Test t7 = static_cast<Test>(1);
    
    cout << "hello world!\n";
    
    
    //匿名对象
    Test("hello world");
     
    Test t10 = getTest(); //调用的为移动拷贝构造函数
    Test t11 = (getTest()); //同上
    Test t12 = Test("hello world"); //匿名对象不会调用 
    return 0;
}

//输出先按顺序输出有参数与无参数构造函数等,最后有几个函数执行几次析构函数

析构函数:

析构函数:释放资源释放空间

特点:

1.无返回值

2.函数名为:~类名

3.无参

4.不能重载

5.当对象离开所在作用域释放空间时,先调用析构函数

.h文件

#pragma once

class Stack
{
    public:
        ~Stack();
}

.cpp文件

~Stack::~Stack() //用于释放函数,出了作用域自动调用
{
    delete [] stack;
    stack = nullptr;
}

this指针:

在类内,每实例化一个对象都有一个指针指向该对象的指针(this)

对象模型:每个对象都有自己独立的属性空间,属性指的是成员变量:按照成员变量所占空间的总和分配空间,共享代码空间(方法,函数)

作用:区别同一个类定义的不同实例

main.cpp

int main()
{
    Stack s1;
    Stack s2;
    Stack s3;
    //用一个类实例化三个对象
    
    s1.push(5);
    //如何知道访问的函数是访问哪个成员的数据?
    //系统内实际输入s1.push(5,&s1);在默认传参时会传入当前变量的地址this指针
}

Stack::Stack()//凡是通过this访问的都是自己的属性
{
    this->stack = new int[10];//标准写法加this
    this->maxLen = len;
    this->top = -1;
}

初始化列表:

一般在函数体内部给成员初始化

初始化列表真正实现的功能:定义并初始化(调用时机:先于构造函数),初始化效率优于构造函数体里初始化(不用发生函数调用)

必须在初始化列表初始化的:

const成员

引用

对象成员(无参构造函数) 实例时对象会自动调用构造函数(定义并初始化)

#include<iostream>

using namespace std;

class A
{
  public:
    int m_num;
    A(int num) : m_num(num)
    {
    }

    ~A()
    {
    }
        
};
class Test
{
    public:
        int m_num;
        char *ptr;
        const int count;//const 成员 只能在初始化列表中初始化
        int &r_num; //初始化会出错,为左值 只能在初始化列表中初始化
        int &&r_num; //引用就不报错
        A a; //对象成员(无参构造函数)
        
        Test()
        {
            this->m_num = 0;
        }

        Test(int num, char *ptr)
        {
            this-> m_num = num;
            this -> ptr = ptr;
        }

        //上下相同,下方使用初始化列表
        Test() : m_num(0),count(0),r_num(0),a(5)
        //r_num(m_num) 也可以
        {
            
        }

        Test(int num,char * ptr) :m_num(num),ptr(ptr)
        {
            
        }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值