C++ (二)

    科技2022-08-18  107

    1.C++的动态内存

    new/delete new[]/delete[] 操作符 new和malloc的区别?

    2.C++的引用

    引用的底层实现其实就是指针 引用必须初始化 一经初始化不能更改目标 引用不能为空 引用提高传参效率,节省内省,比指针操作简单 引用与指针的区别?

    3.C++的类型转换

    隐式类型转换(自动) 强制类型转换 显示类型转换: static_cast<>() const_cast<>() reinterpret_cast<>() dynamic_cast<>()

    4.C++面向对象

    类: 一为事物的抽象描述 泛化的概念 泛指 对象: 类实例化具体化的结果 特指 抽象: 把事物共同的特征抽象为属性 把事物共同的行为抽象为方法 封装: 把一类事物用类的语言来描述,加以访问控制属性的限制 public\protected\private
    类的语法:
    class 类名{ //成员属性 //成员方法 //构造方法 函数 //没有返回值类型(也不能是void) 函数名和类名一样 可以重载 有初始化列表 //如果一个类没有提供构造方法,那么会自动添加一个无参构造方法 一旦显式提供则不再提供默认的无参构造 //初始化列表的执行顺序只与成员属性的声明顺序有关 类名(形参列表):初始化列表{ } //成员方法中有一个隐含的this指针 指向正在调用或者构造的那个对象 //当成员变量与局部变量同时名 可以用 this-> 来访问成员变量 //常方法 返回值类型 方法名(形参列表)const{ //const修饰的是 this 指针所指向的对象 } };
    对象的实例化
    类名(实参列表); 类名 变量名(实参列表); 类名 变量名; 类名 变量名(); //不是实例化对象 而是声明一个函数 new 类名; new 类名(); new 类名(实参列表);
    常对象: 具有常属性的对象const
    常对象只能调用常方法(不能调用非常成员方法) 非常对象 可以 自动提升为 常对象 但是常对象的 const 属性 不能随便去掉 常版本与非常版本的 成员方法 构成重载 非常对象如果在调用时 有 非常版本的函数 调用 非常版本函数 如果没有则也可以调用常版本的函数
    对于重载:
    参数列表不同: 参数类型不同: 指针和引用的常属性不同也构成重载 常成员方法中不能对成员属性进行修改 const *this 但是如果用mutable关键字修饰的成员属性 就可以被修改
    静态属性(类属性) 和 静态方法(类方法)
    属性类 而不属于某个对象 所有该类的对象共同拥有一份 类属性存储位置和普通属性存储的位置不一样 静态属性必须在类外声明初始化 静态方法中没有隐含的this指针 所以在静态方法中不能访问成员属性和调用成员方法 静态方法中只能访问静态属性和调用静态方法 静态属性和静态方法也可以通过普通对象来访问和调用 静态属性和静态方法相当于是加了类作用域的全局变量和函数
    单参构造:
    如果一个类有单参构造函数 A(B b){} 那么任何B类型的对象都可以自动(调用该构造函数)转换为A类型对象 如果要禁止自动(必须明确调用构造函数 A(b)) 则在构造方法前加 explicit
    必须使用初始化列表的情况:
    1.常属性成员变量 2.引用型成员变量 3.没有无参构造的类类型的成员变量 构造对象时,先按照声明顺序去构造 成员变量,即调用每个对象所属类的无参构造函数 最后执行本类的构造函数体 析构时 正好和构造的顺序相反
    5.析构函数
    一个类有一个默认的无参构造函数 该函数没有实现任何功能 当一个对象消亡或者 delete时 就会调用该构造函数 所以当一个对象在构造时如果new的动态内存,那么就需要自己手动实现析构函数 在析构函数中释放之前new申请的动态内存 析构函数: 没有返回值类型 也不是void ~类名(){ } 析构函数不能重载 因为析构无参
    6.拷贝构造函数(深拷贝 、 浅拷贝)
    用一个已经存在的对象来构造一个新的同类型的对象 调用的就是拷贝构造函数 所谓拷贝构造 本质 就是复制克隆一个新对象 编译器会自动给类提供一个拷贝构造函数 拷贝构造函数形如: 类名(const 类名& other){ } 默认的拷贝构造函数实现的方式是按字节拷贝(memcpy) 构造的新对象 和 旧对象是完全一模一样 按字节拷贝持方式是浅拷贝 深拷贝: 拷贝指针所指向内存地址中的数据 而不是拷贝指针的值本身 之间旧的C++版本对同一块动态内存多次delete会出错 如果一个类需要实现深拷贝的功能 则需要实现拷贝构造函数 用一个旧的对象来构造一个新的对象 类名 对象 = 旧对象; 类名 对象(旧对象); void func(类名 obj){} func(对象); 旧对象 = 旧对象; 两个对象都已经存在了,则不会调用拷贝构造 对象之间可以用 = 进行相互赋值 默认的赋值是按字节拷贝的形式进行赋值 如果是指针 则复制指针的值 并不是把指针所指向内存中的数据进行复制
    7.拷贝赋值函数
    如果没有提供拷贝赋值函数 则编译器会自动提供拷贝赋值函数 拷贝赋值函数默认实现也是按字节复制(浅拷贝) 如果有需要实现深拷贝,则需要自己实现 形如: 类名& operator=(const 类名& other){ }
    8.string
    class string{ private: char *s; public: string(const char *str); ~string(); string(const string& str); string& operator=(cosnt string& str); };
    8.默认的无参构造 拷贝构造 拷贝赋值函数
    默认无参 调用 成员的无参构造 如果需要调用指定的有参 需要在初始化列表 拷贝构造 调用 成员的拷贝构造 进行 构造 拷贝赋值 调用 成员的拷贝赋值函数 进行 赋值 在实现拷贝构造和拷贝赋值函数的时候,类类型成员需要手动调用拷贝构造 和 拷贝赋赋值 class string{ private: char *str; public: string(const char* s=""):str(strcpy(new char[s==NULL?1:strlen(s)+1],s==NULL?"":s)){ /* size_t len = s==NULL?1:strlen(s)+1; str = new char[len]; strcpy(str,s==NULL?"":s); */ } string(const string& s):str(strcpy(new char[strlen(s.str)+1],s.str)){ /* str = new char[strlen(s.str)+1]; strcpy(str,s.str); */ } string& operator=(const string& s){ if(this != &s){ string stmp(s); swap(str,stmp.str); } return *this; } ~string(){ if(str != NULL){ delete [] str; str = NULL; } } };
    9.成员属性指针 成员函数指针
    定义成员属性指针变量 属性类型 类名::*指针变量; 初始化 or 赋值 属性类型 类名::*指针变量 = &类名::属性名; //不是一个真实的地址 指针变量 = &类名::属性名; 成员指针直接解引用 对象 对象.*成员指针 成员指针间接解引用 对象的指针 对象的指针->*成员指针 定义成员函数指针变量 成员函数返回值 (类名::*指针)(成员函数形参列表); 初始化 or 赋值 成员函数返回值 (类名::*指针)(成员函数形参列表) = &类名::成员函数名; 指针 = &类名::成员函数名; 成员函数指针直接解引用 对象 (对象.*成员函数指针)(实参列表); 成员函数指针间接解引用 对象的指针 (对象的指针->*成员函数指针)(实参列表);

    2. friend

    在一个类里面 可以声明另外的类为该类的友元类 在友元类中可以访问该类对象的私有属性和私有函数 也可以声明一个全局函数为该类的友元函数 则在友元函数中也可以访问该对象的私有内容 友元的声明是单向性的

    3.运算符重载

    在对应的class中提供对应的 运算符函数 来实现功能 本质上是一个函数 返回值类型 operatorX(形参列表){ } 拷贝赋值: 重载=运算符 重载输入输出运算符: 只能以友元函数的形式重载 //重载输出运算符 friend ostream& operator<<(ostream& os,const 类名& 对象){ } //重载输入运算符 friend istream& operator>>(istream& is,类名& 对象){ } 对运算符进行分类 按操作数的个: 1.单目运算符 +(正) -(负) *(取值) &(取地址) !(逻辑非) ~(按位取反) ++ -- #obj; ---> obj.operator#() 2.双目运算符 << >> 两个操作数对象的类型不一样 cout << n; cout n cin >> n; o1 # o2 o1[o2] --> o1.operator(o2) cout.operator<<(stu) ostream类型早已封装好了 不能给ostream类重载这样一个成员函数 << 和 >> 运算符 只能以全局变量的形式进行重载 3.三目运算符 ?: 三目运算符不可以进行重载 以成员的方式重载运算符 第一个操作对象隐身为*this 单目运算符以成员方式重载 没有 参数 双目运算符以成员方式重载 只有一个参数 写个Complex类 实现 + - * / << >> + - 以友元方式重载 * / 以成员方式重载 - 重载负数运算符 ~ 调换实部和虚部 ++ -- 运算符重载 用哑元来区分前++--和后++-- 类名& operator++(){} const 类名 operator++(int){} [] 一般来说都会实现一个常版本 和一个非常版本 () 重载()运算符的类的对象 称为 函数对象 因为这种类型的对象 可以像函数一样调用 C语言中用函数指针来实现回调 C++中用函数对象来实现回调 A类中有B作为参数的单参构造函数 B类型对象---> A类型的对象 class A{ public: A(B& b){} }; 重载类型运算符: class A{ operator B(){ return B类型对象; } } A类型对象都可以自动转换成B类型的对象 注意: A 类型对象怎么可以自动转换为 B类型的对象? A->B 1. 在B类中添加A类型的单参构造 2. 在A类中重载B类型的类型运算符 new/delete new[]/delete[] 可以以全局函数的方式重载 也可以以类的静态方式重载 注意: == 和 != 成对出现 排序 查找 映射 需要重载 < 1.不能重载的运算符 . :: sizeof typeid .* 2.只能以友元方式重载 << >> 3.只能以成员方式重载 = [] () 4.不能创建新的运算符 x**y 5.运算符重载应该保持其原有的含义和运算规则
    Processed: 0.019, SQL: 9