C++复习笔记3——类与对象

  • C++三大特征
    • 封装
    • 继承
    • 多态
  • 类与对象
    • 1.C++ struct和class的区别
    • 2.对象和变量的区别
    • 3.面向过程和面向对象的区别
  • OPP思想
  • 封装
  • this指针
  • 类中默认的函数
      • 构造函数
      • 析构函数
      • 拷贝构造函数
      • 赋值运算重载
        • 内存泄漏的情况:
    • 取地址操作符的重载函数
    • const修饰的取地址操作符的重载函数
  • Static静态成员变量
  • 临时量
    • 生存周期为表达式结束
    • 优化
      • 赋值
      • 临时量在赋值运算符重载中的应用
      • 示例代码:
  • 学习代码(仅用来举例学习本博客内容)

C++三大特征

封装

继承

多态

类与对象

1.C++ struct和class的区别

/ / /
struct 默认 public
class 默认 private

2.对象和变量的区别

对象就是内存+资源
调用对象时做的事是开辟内存并初始化
变量就是内存
使用变量做的事仅仅是开辟了内存

3.面向过程和面向对象的区别

https://blog.csdn.net/abcdnml/article/details/75329520

OPP思想

封装

/ /
public 任意位置都可以访问
protected 只有本类和子类可访问
privated 只有本类可访问

【我想访问私有成员可以通过访问公有成员来访问】
【属性和实现细节被隐藏起来,对外来提供公有接口来调用】??

this指针

类成员方法的调用约定 _thiscall;

类中默认的函数

类中默认的函数(如果有写就会用写的,没有写就会用系统提供的):

构造函数

  1. 初始化对象所占用的内存 赋予资源
  2. 可以重载
  3. 不依赖对象调用
  4. 不用手动调用//因为调用的时候对象还没生成 如同鸡生蛋蛋生鸡那样

析构函数

  1. 释放对象所占用的资源
  2. 不可重载
  3. 手动调用 退化成普通函数的调用//系统本身会调用,如果使用自己写的析构函数还需要在main里自己手动调用一次 就像普通函数的调用一样
     ~String(){delete[] mptr;//释放mptr指向的空间mptr = NULL;//置空指针}

拷贝构造函数

  1. 拿已经存在的对象来生成相同类型的新对象
  2. 形参 一定要用引用的,因为要避免递归调用生成形参对象最终导致栈溢出程序崩溃。
    【不写拷贝构造系统也可以自己纪念性拷贝构造,但会崩溃因为默认的拷贝构造函数是一个浅拷贝函数】
    【浅拷贝:多个对象拥有一个资源 】
    【深拷贝:每个对象都有自己的资源 】
    如:
  Student(const Student& rhs)//拷贝构造函数 形参要用引用 {mid = rhs.mid;mname = rhs.mname;msex = rhs.msex;mage = rhs.mage;}//拷贝构造函数int main(){Student stu1;stu2=stu1;//拷贝构造函数}

赋值运算重载

  1. 拿已经存在的对象给相同类型的已存在赋值
  2. 返回值为 类 类型的引用 (此处是因为这样做可以连续赋值)
  3. 赋值运算符重载函数是浅拷贝函数
  4. 该函数要做的事有:(如例2)
    (1)自赋值
    (2)释放旧资源
    (3)开辟新资源(内存)
    (4)赋值
  • 例1(不安全的
 Student& operator=(const Student& rhs){if (this != &rhs)//这里进行自赋值判断是为了减少自赋值如果生成的是对象会占用资源 //所以不想浪费资源选择返回this指针。{mid = rhs.mid;mname = rhs.mname;msex = rhs.msex;mage = rhs.mage;}return *this;//如果是自赋值返回this指针}//这是一种不安全的赋值运算符重载,但如果私有成员中没有指针是可以的 //如果私有成员中存在指针 该函数将会造成内存泄漏的严重情况

内存泄漏的情况:

String& operator=(const String& rhs)//赋值运算符重载函数
{if (this != &rhs){mptr=rhs.mptr;}return *this
}//例1的相同方法用在私有成员是指针的情况
int main()
{String str1("hello");String str2;str2 = str1;//如果此时主函数这样调用 然而赋值函数用的是例一的方法 就会发生内存泄漏return 0;
}

例2(相对安全的,也存在不安全的情况,这是一种私有成员内有指针的情况)

class String
{
public:String()//构造函数{mptr = new char[1]();}String(char* ptr){mptr = new char[strlen(ptr) + 1]();strcpy_s(mptr, strlen(ptr) + 1, ptr);}String(const String& rhs)//拷贝构造函数{mptr = new char[strlen(rhs.mptr) + 1]();strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);}String& operator=(const String& rhs)//赋值运算符重载函数{if (this != &rhs){delete[] mptr;mptr=nullptr;//释放mptr的内存空间,此时mptr为NULLmptr = new char[strlen(rhs.mptr) + 1]();//给mtpr开辟新内存 大小为赋值源对象的长度+1/*此处上面两个函数会有一种不安全的情况:如果先释放mptr内存空间,但是内存不足导致开辟内存失败了new char抛出异常 就会导致丢失了整个对象 mprt就是一个空指针,非常容易导致程序崩溃,是一个很严重的问题。*///这里应该先开辟内存再释放mptr空间,防止内存开辟失败导致丢失这个对象//--->剑指offer面试题1(会写在例3中)strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);//c++认为strcpy不安全,使用strcpy_s(目的,字节长度,源)可以改变这个不安全的情况}return *this /*自赋值释放旧的资源开辟新的资源赋值,*/}
private:char* mptr;
}

例3(更加安全,考虑到异常的解法)
剑指offer面试题1:赋值运算符重载

String& operator=(const String& rhs)
{if(this!=&rhs){String strTemp(rhs);//构建一个临时的实例 生存周期仅为该函数结束char*pTemp = strTemp.mptr;//建一个指针指向临时实例strTemp.mptr=mptr;//把mptr给临时实例mptr=pTemp;//最后}
}

这个方法先创建一个临时实例,在交换临时实例和原来的实例,在这个函数中我们先创建一个临时实例strTemp,接着把strTemp.mptr和实例自身的mptr进行交换。由于strTemp.mptr是一个局部变量,但程序运行到if的外面时也就出了该变量的作用域就会被自动调用该实例的析构函数把strTemp.mptr指向的内存释放掉。由于strTemp.mptr指向的内存就是实力之前mptr的内存,这就相当于自己调用析构函数释放实例的内存。

取地址操作符的重载函数

const修饰的取地址操作符的重载函数

Static静态成员变量

  • static修饰成员变量
    1. 不属于对象 属于类作用域
    2. 一定要在类外初始化
//类内:
static int mb;
//类外
int Test::mb=10;//(Test为类名)
  • static修饰成员方法
    1. _cdecl 没有thiscall(this指针) 不依赖对象调用
    2. 不能调用普通的类成员(包括成员变量和成员方法)
    3. 不能访问除静态成员变量之外的变量

临时量

  1. 内置类型的临时量 常量
  2. 自定义类型的临时量 变量
  3. 隐式生成的临时对象 常量
    生存周期为表达式结束
    优化
    1. 如果临时对象的生成目的是生成新对象
    2. 如果是想以生成临时对象的方式生成新对象

【引用提升临时对象的生存周期 】

  • const static修饰成员

  • const 修饰成员变量

  • 1.临时对象生成 构造函数

  • 2.新对象生成 拷贝构造

  • 3.临时对象销毁 析构函数

赋值

赋值先看类型匹配:

  1. 类型是否匹配,匹配就赋值不匹配往下走
  2. 隐式转换:看一下类中是否有像我一样的构造函数可以构造成同类型,有则可以生成临时对象来调用构造函数构造一个同类型的进行赋值
  3. 强制转化
  4. 报错(临时量分为有优化和没有优化)
临时量在赋值运算符重载中的应用

x’x’l’l’l’l’l’l’l’l’l’l’l’l’l’l’l’l’l
main()函数

  1. Test test1;
    test1=30;
    //30和test1不是同类型生成一个临时对象调用里面的构造函数然后构造一个临时对象然后就可以赋值了
  2. Test &rt2=Test(10);
    引用提升了临时对象生存周期,临时对象生存周期由原本的到表达式结束变为到函数结束,生存周期延长。?
  3. Test *pt3=&Test(10);
    //构造函数+析构函数
  4. const Test&rt3=10;
    //10为常量,常引用可以,10生成临时对象并起名为rt3。如果不加const是不对的。
示例代码:
class Test
{
public:Test(int a = 0) :ma(a){std::cout << "Test::Test(int)" << std::endl;}Test(const Test& rhs){std::cout << "Test::Test(const Test&)" << std::endl;ma = rhs.ma;}~Test(){std::cout << "Test::~Test()" << std::endl;}Test& operator = (const Test& rhs){std::cout << "Test::operator=(const Test&)" << std::endl;if (this != &rhs){ma = rhs.ma;}return *this;}
private:int ma;
};Test gtest1(10);
int main()
{Test ltest1(10);//main函数结束//调用点到当前作用域结束Test ltest2(ltest1); //拷贝构造函数ltest1 = 20;//隐式生成临时对象调用构造函数ltest2 = Test(20);//显示生成一个临时对象 临时对象无优化ltest2 = (Test)(34, 34, 67, 67789, 654);//只赋值654逗号表达式654-》ltest2Test* ptest1 = new Test();delete ptest1;//在堆上不遵循先构造后析构 delete结束了new开辟的就结束了Test* ptest2 = new Test[10];//?delete[] ptest2; Test& rt1 = ltest1;//给对象ltest起别名叫rt1std::cout << "--------------------" << std::endl;Test& rt2 = Test(10);//给显示生成的临时对象起别名为rt2 std::cout << "--------------------" << std::endl;Test* ptest3 = &Test(10);//?????/???????std::cout << "--------------------" << std::endl;const Test& rt3 = 10;//常引用,10生成临时变量并起名为rt3????return 0;
}
Test gtest2(20);int main()
{//const int& a = 10;Test test1(20);//建立对象并传参初始化 构造函数+析构test1 = 20;//20生成了一个临时对象调用了构造函数并赋值 构造+赋值+析构test1 = Test(20);//显示生成了临时对象 构造函数+赋值+析构//test1 = 30;// Test  <=  int   //Test <== Test //std::cout << "--------------------" << std::endl;//Test test2 = 20;// Test  <== int   //Test <== Test//std::cout << "--------------------" << std::endl;/*1.临时对象生成  构造函数2.新对象生成    拷贝构造3.临时对象销毁  析构函数*///int a = 10.1;//int  = double  return 0;
}

学习代码(仅用来举例学习本博客内容)

#include<iostream>
class String
{
public:String()//构造函数{mptr = new char[1]();}String(char* ptr){mptr = new char[strlen(ptr) + 1]();strcpy_s(mptr, strlen(ptr) + 1, ptr);}String(const String& rhs)//拷贝构造函数{mptr = new char[strlen(rhs.mptr) + 1]();strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);}String& operator=(const String& rhs)//赋值运算符重载函数{if (this != &rhs){mptr = new char[strlen(rhs.mptr) + 1]();//先开辟内存后释放空间delete[] mptr;//这里应该先开辟内存再释放mptr空间,防止内存开辟失败就会丢失这个对象strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);//c++认为strcpy不安全,使用strcpy_s(目的,字节长度,源)可以改变这个不安全的情况}return *this /*自赋值释放旧的资源开辟新的资源赋值,*/}~String(){delete[] mptr;//释放mptr指向的空间mptr = NULL;//置空指针}private:char* mptr;
int main()
{String str1("hello");String str2;str2 = str1;return 0;
}class Student
{
public:Student(){}Student(const std::string& id, const std::string& name, bool sex, int age):mid(id), mname(name), msex(sex), mage(age){}Student(const Student& rhs){mid = rhs.mid;mname = rhs.mname;msex = rhs.msex;mage = rhs.mage;}Student& operator=(const Student& rhs){if (this != &rhs){mid = rhs.mid;mname = rhs.mname;msex = rhs.msex;mage = rhs.mage;}return *this;}~Student(){std::cout << "Student::~Student()" << std::endl;}void eat(const std::string& meal)//Student* const this{std::cout << this->mname << " is eatting " << meal << std::endl;}void sleep(const std::string& time){std::cout << mname << " is sleeping at " << time << std::endl;}void solve(const std::string& math){std::cout << mname << " is doing this " << math << std::endl;}
private:std::string mid;std::string mname;bool msex;int mage;
};
int main()
{//int a = 10;//int b = 20;//a = b;Student stu1("001", "zhangsan", true, 19);Student stu2;stu2 = stu1;return 0;
}int main()
{Student stu1("001", "zhangsan", true, 19);Student stu2 = stu1;int a = 10;int b = a;return 0;
}int main()
{Student stu1;stu1.eat("肉");stu1.~Student();return 0;
}
#endif

C++复习笔记3——类与对象(赋值重载、临时对象、const、static)相关推荐

  1. 浅谈将子类对象赋值给父类对象

    最近对将子类对象赋值给父类对象有点心得,想和大家分享一下,但本人水平有限,请各位指正和批评.言归正传,下面是几个小例子,请大家看一看. 测试一 父类: public class Supclass {p ...

  2. 复习笔记(六)——C++运算符重载(难点)

    运算符重载 运算符重载的概念 运算符重载类似于函数重载. 运算符重载允许把标准运算符(如+.-.*.<等)应用于定制数据类型的对象. 什么情况下需要考虑运算符重载? 需要用运算符操作自定义类的对 ...

  3. js对象赋值、循环对象

    一般我们给对象的属性赋值时这样的: let a={}; a.value="值"; 如果在一个循环中,循环去给对象赋值的话,就会覆盖掉前面的值,而又想每次循环赋值的属性都不一样的话, ...

  4. 嵌入式软件开发培训笔记——Java第三天(方法重载、对象的构造与初始化过程分析、封装等)

    一.掌握方法重载(Overload)     同一个类中方法名相同时,称为方法的重载(Overload)     特点:1.参数列表不同                     1)参数类型不同   ...

  5. 复习笔记(二)——C++面向对象设计和使用

    面向对象编程(OOP) 面向对象(Object Oriented )是认识事务的一种方法,是一种以对象为中心的思维方式 面向对象的程序设计: 对象=(算法+数据结构) 程序=对象+对象+--+对象 面 ...

  6. 两个不相同的类之间如果赋值

    /// <summary> /// 类属性/字段的值复制工具 /// </summary> public class ClassValueCopier{/// <summ ...

  7. 8-2:C++继承之父类和子类对象赋值转换(公有继承)也即切片

    父类和子类对象赋值转换(公有继承) - - - 父类和子类对象赋值转换是指:子类对象可以赋值给父类对象(父类的指针或引用也可以),而父类对象不能赋值给子类对象.也就Student一定是Person,但 ...

  8. C++本质:类的赋值运算符=的重载,以及深拷贝和浅拷贝

    关键词:构造函数,浅拷贝,深拷贝,堆栈(stack),堆heap,赋值运算符 摘要:     在面向对象程序设计中,对象间的相互拷贝和赋值是经常进行的操作.     如果对象在申明的同时马上进行的初始 ...

  9. 临时对象与NRV技术

    临时对象与NRV技术 <More Effective C++>中讲到,在C++中真正的临时对象是看不见的,它们不出现在你的源代码中.建立一个没有命名的非堆(non-heap)对象会产生临时 ...

  10. 第八天2017/04/17(1、拷贝构造、❤临时对象)

    调用拷贝构造函数的三种case 1. MyPoint p2 = p1; MyPoint p2(p1);case1中的重点:MyPoint p3 = MyPoint(3,3); //此时:C++编译器进 ...

最新文章

  1. 罗兰贝格84页白皮书:一文看懂中国ICT产业新黄金十年
  2. 高分辨率下IE浏览器缩放导致出现右侧滚动条问题的解决
  3. #100天计划# 2013年10月14日
  4. ssm 使用中的一些问题
  5. 手机KG音乐怎么下载竖屏MV
  6. ORACLE中的两个概念:user和schema的区别和联系
  7. jsp连接mysql数据库代码_JSP连接MySQL数据库代码
  8. Map.putAll方法——追加另一个Map对象到当前Map集合
  9. Ubuntu中MySQL的启动/关闭/重启
  10. 【SRH】------node创建简单的服务器
  11. webstrom html校验css语法,webstorm ECMAScript 6 语法检查
  12. PR视频转场预设 10个快节奏极限运动空间扭曲效果PR转场过渡预设
  13. capslock键英语怎么读_capslock怎么读
  14. Visual Studio 2019重新安装问题
  15. 还在到处寻找画图软件?快来用Typora画饼图、时序图、流程图、UML图和状态图吧
  16. PEPL命令-交互式解释器
  17. 关于HTTP协议、万维网文档以及网络编程的基础梳理
  18. TCP序列号和确认号
  19. zstuoj (浙理工) 孙壕请一盘青岛大虾呗
  20. 若依框架引入外部字体(思源黑体)

热门文章

  1. VirtualLab初学者教程-2.搭建一个光学系统
  2. 基于STM32单片机的精彩设计实例合集
  3. angular 使用jsMind
  4. win10浏览器闪退_win10自带浏览器闪退解决方法
  5. Matlab代码格式一键美化神器
  6. 服务器虚拟化的重要性,服务器虚拟化:虚拟机迁移的重要性
  7. win10硬盘锁怎么解除_Win10磁盘被写保护怎么解除?
  8. 复工复产到欧洲,深兰科技环卫产品亮相国际舞台
  9. Simulink仿真---clark变换、反clark变换
  10. php手机网页在线录音ios,HTML5网页录音和上传到服务器,支持PC、Android,支持IOS微信...