栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++之面向对象、构造函数、析构函数

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++之面向对象、构造函数、析构函数

面向对象

面向过程:把问题分解成若干步骤,把每个步骤封装成函数,然后依次调用各个函数

  • 面向对象:把问题看作对象之间的相互作用
  • 如何把一头大象放进冰箱
    • 创建大象和冰箱
    • 冰箱自动打开
    • 大象能进入冰箱
    • 冰箱自动关门
  • 面型对象编程中,一切都是类和对象
类和对象

类——类型 一类事物的统称,泛化的抽象概念

  • 如:我想买一台电脑 ——电脑 -> 类

对象——变量 ;类实例化的结果,特指一个原本存在的事物

  • 如:我正使用的电脑坏了 ——电脑->对象

用类定义对象的过程,称为实例化

  • C++中定义类可以使用struct 也可以是class

    class 类名{
    //访问控制属性:
    	//属性
        //方法
    };
    
    //实例
    class Complex{
    public:
        //属性 特征
        double r;//实部
        double v;//虚部
    public:
        //方法
        void show(void){
    		cout << r << "+" << v << "i" <
        //实例化
    	Complex *ss = new Complex;
    }
    
面向对象的三大特征
  • 封装

    把一类事物都拥有的特征抽象为属性,把一类事物都具有的行为抽象为方法, 并加以访问可控制的属性限制,我们称之为封装

    • 访问控制属性

    • public 公开的

    • protected 受保护的

    • private 私有的

    • 一般情况下,属性会设置为受保护的或私有 的,方法会设置公开的,也会为私有属性提供公开的set和get

    • 访问控制属性类中类外子类
      public可以访问可以访问可以访问
      pretected可以访问不能访问可以访问
      private可以访问不能访问不能访问
  • 继承

  • 多态

class 类型名{
	//属性
    
    //方法       可以直接访问成员属性
};
实例化对象
  • 用类型定义变量

  • 类名 对象名 实例化的对象若不经过初始化,则是垃圾值,需要额外的赋值语句

  • 如果需要在构造对象时,给对象进行初始化,需要在定义类时,添加构造函数

  • 实例化对象一定要调用构造函数,如果一个类没有构造函数,则编译器会自动安排一个无参数的构造函数

  •   //无参构造实例化对象     在栈内存实例化对象
      ObjType  obj;   //调用无参构造实例化对象
      ObjType  obj(); //函数声明    声明一个函数名为obj   返回值类型为ObjType,且没有任何参数  ***!!!
      
      ObjType  obj(arg,..);   //调用有参构造函数
      
    

//C++11
ObjType obj{}; //调用无参构造
ObjType Obj{arg,…}; //调用有参构造

//在堆内存中实例化对象
ObjType *po = new ObjType;    //调用无参构造
ObjType *po = new ObjType();  //调用无参构造
ObjType *po = new ObjType{};  //调用无参构造
ObjType *po = new ObjType(arg,...);
ObjType *po = new ObjType{arg,...};

​

//匿名对象         没有变量(对象)名的对象  
ObjType();              //调用无参构造函数实例化一个匿名对象        
ObjType(arg,...);

```
构造函数

在实例化对象时调用的函数,称为构造函数

class 类{
public:
	//构造函数
    类名(形参列表){

    }
};
  1. 构造函数没有返回值,也不能声名为void

  2. 构造函数的函数名和类型名是一样的

  3. 如果一个类没有实现构造函数,则编译器会自动生成一个无参的构造函数

  4. 如果一个类中有提供构造函数,则编译器不会为其提供默认无参构造

    Complex obj;//无参
    Complex obj();//声名函数,返回值类型为Complex, 没有任何参数
    Complex obj{};//无参
    Complex obj{123};//有参
    
  5. 构造函数允许重载

    重载构造函数,可以不同的需求来实例化对象

  6. 构造函数的任务基本上是对属性初始化

    通过类实例化对象,一定需要匹配的构造函数,否则就会编译报错

class rectangle{
public:
    char c;
    int lonth;
    int wide;
public:
    rectangle(char _c, int _lonth, int _wide){
		c = _c;
        lonth = _lonth;
        wide = _wide;
    }
public:
    void make_it()
    {
		for(int i = 0; i < lonth ;i++){
			for(int j = 0; j < wide; j++){
				if(j != 0 || j != wide-1){
					if(i==0 || i == lonth){
						cout<
		return lonth * wide;
    }
    void show()
    {
		cout<<"lonth:"< 

struct和class在C++中的区别

  • class访问控制属性是private, struct是public
友元

可以声明全局函数有友元函数,那么在友元函数中可以访问该类的私有/保护成员

class 类名{
    friend 友元函数声名 
};
  • 可以声明类为友元类,那么在友元类中可以访问该类的私有/保护成员

    • 友元类的声明是单面的
  • 友元其实是打破了封装的效果,但某些情况不得已而为之

this

本质是一个指针常量

  • 当成员属性名和成员函数中的局部变量同名时,遵循局部变量优先原则,但是我们可以用this->成员名的方法访问类成员属性
  • 在所有的成员函数中都隐含一个this指针,指向正调用这个函数的对象,但是类中没有冲突的情况下可以省略
  • obj.func(); 底层实现会转换为 func(&obj); func(Type *const this);
构造函数初始化列表

只有沟槽函数才有初始化列表

class 类名{
	类名(形参列表):初始化列表{
        
    }//像这样:stu(int id, string name, int age):id(id), name(name), age(age)
}

只有在初始化列表中才成为初始化,在构造函数内部则称为赋值,如果有const修饰的或者为引用的成员,则必须用初始化列表进行初始化

class A{
private:
    cosnt int a;//常属性成员
    int &b;//引用
public:
    A(int a,int &b):a(a), b(b){}
}
  •   class B{
      private:
          int n;
          int m;
      public:
          B(int a=1024):m(a),n(m){
              
          }
      int getN(){
              return n;
          }
          int getM(){
              return m;
          }
      };
      
      B b;
      cout << b.getN() << ":" << b.getM() << endl;
    
  • 初始化列表执行顺序与初始化列表的书写顺序无关,只与定义顺序相关

组合

关于成员有类类型时,在构造函数的初始化列表中,默认调通类类型成员的无参构造进行初始化,也可以在成员中传递构造函数的参数

class Point{
    double x;
    double y;
public:
    Point(double x,double y):x(x),y(y){
        
    }
};

class Circle{
    Point center;
    double r;
public:
    //center(0,0)即调用 Point(double x,double y)有参构造
    Circle():center(0,0){
        
    }
};

int main(){
    Circle c;  //编译报错          解决方案有2:   1.让Point提供无参构造   2.在Circle构造函数的初始化列表中显示调用Point的有参构造
    return 0;
}

构造函数的初始化列表中,如果有必要调用有参构造函数进行构造成员,需要显式调用

构造函数的执行顺序:先按照类类型的成员的定义顺序依次调用构造函数,然后执行构造函数体

可以利用构造函数和析构函数,在main函数之前、之后进行输出

class A{
public:
    A(){
        cout << "A()" << endl;
    }
    ~A(){
        cout << "~A()" << endl;
    }
};
A obj;  //在main begin之前输出 A()   在main end之后输出 ~A()
int main(){
    cout << "main begin" << endl;
    cout << "main end" << endl;
    return 0;
}
代理构造(消失了?!
  • 在一个类的初始化列表中,调用另一个类的构造函数
单参构造
  • 如果一个类A提供了一个T类型的单参构造函数,那么T类型的数据可以隐式调用该单参构造函数转换为A类型的对象
  • 如果需要禁止T类型数据隐式调用单参构造函数转换为A类型的对象,可以在A类型的单参构造函数前加上explicit关键字
  • 加上explicit之后就不能隐式进行,只能显示调用构造函数进行转换
静态属性

属性用static修饰,称为静态属性

  • 静态属性需要在类外进行定义

  •   class 类名{
      public:
          static int s;//声明      没有分配内存空间
      };
      
      int 类名::s = 1024;   //在类外定义和初始化
    
  • 静态属性存储于全局数据区

  • 成员属性一般存在在栈区或者堆区

静态属性属于类而不是某个对象,每个对象都能访问,访问到的都是同一个

静态属性相当于加了 类名: 作用域限定的全局变量

静态属性可以直接用类名:静态属性的方式直接访问

也可以用 对象:静态属性 的方式进行访问

静态方法

方法用static修饰,称为静态方法

静态方法属于类而不是某个对象

  • 相当于加了 类名:作用域限定符 的全局函数
  • 可以直接使用 类型:静态方法名(参数列表)直接调用静态方法
  • 也可以使用 对象:静态方法名(阐述)直接调用
  • 静态方法中不能使用this
    • 静态方法中不能直接访问成员变量,不能直接调用成员函数
  • 静态方法中可以直接访问静态属性和直接调用其他静态方法
常对象和常函数
  • C++中const和C语言中的const是不一样的

    • C语言中的const仅仅是表示只读
    • C++const表示却是常量 g++编译器会对const进行替换处理,在编译时对使用const定义变量进行直接替换,好比C语言中的宏定义 C++中const变量必须初始化
  • 常对象,const修饰的对象 常对象的属性是只读(read only)

  • mutable修饰属性,表示常对象也可以修改该属性的值

  • 常方法/常函数(一定得是成员函数才行)

    class A{
        
        //const 常方法、常函数   const修饰的是   *this
        返回值类型  成员方法名(形参列表)const{
            
        }
    };
    
  • 常对象,只能调用常方法 不能调用非常函数

  • 普通对象能够调用常方法 如果没有非常方法的情况下

  • 常函数和非常函数构成重载

    • 常对象只能调用常方法
    • 普通对象在有非常方法时,选择调用非常方法,如果没有非常方法,则调用常方法
  • 在实践过程中,如果一个方法的常方法和非常方法的实现是一样,且不会修改成员时,都会把方法定义成常方法

析构函数

在对象的生命周期结束时会自动调用析构函数

class A{
public:
    A(){}//构造函数
    ~A(){}//析构函数
}

析构函数没有返回值,也不能是void

析构函数的名字:~类名

析构函数没有参数,不能重载

如果一个类没有实现析构函数,则编译器会自动生成一个析构函数,该析构函数为一个空析构

析构函数不能且无法手动调用,只能在对象消亡时自动调用

  • 栈内存中的对象在栈内存回收时会调用析构
  • 堆内存——>delete
  • 全局变量——>程序结束

社么时候申请析构函数

  • 打开文件/申请动态内存——在析构函数中释放动态内存或关闭文件
    • 避免内存泄漏

析构函数的执行顺序:

  1. 与构造函数相反,
成员属性指针和成员方法指针

成员属性指针:

  • 定义:成员类型 类名::*指针名;
  • 初始化:类名::*指针名 = &类名::属性名;
  • 成员属性指针保存的并不是真实的内存地址
  • 解引用:对象。*成员属性指针
  • 间接解引用 对象指针->*成员属性指针
成员类型  类名::*指针名;

成员类型  类名::*指针名 = &类名::属性名;  

指针名 = &类名::属性名;

成员属性指针保存的并不是真实的内存地址  (1)

--直接成员指针解引用    对象
    对象.*成员属性指针    
--间接成员指针解引用    对象的指针
    对象指针->*成员属性指针

成员方法指针:

//成员方法指针定义
成员函数返回值类型   (类名::*pf)(形参列表);

成员函数返回值类型   (类名::*pf)(形参列表) = &类名::方法名;

pf = &类名::方法名;

(对象.*pf)(实参列表);

(对象指针->*pf)(实参列表);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1029880.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号