C++

C++ 类和对象

类class

定义::具有相同属性和行为的对象的集合(人类,就是一个人的集合,每个单独的个体就是这个类的叫对象)

类的基本形式
class 类名
{
	public:
	//行为或属性 
	protected:
	//行为或属性
	private:
	//行为或属性
};

类中包含的几个概念

  • 类成员:
    类可以没有成员,也可以定义多个成员。成员可以是数据、函数或类型别名。所有的成员都必须在类的内部声明。
    没有成员的类是空类,空类也占用空间。
  • 成员函数
    成员函数必须在类内部声明,可以在类内部定义,也可以在类外部定义。如果在类内部定义,就默认是内联函数。
  • 构造函数
    构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
  • 析构函数
    析构函数为成员函数的一种,名字与类名相同,在前面加‘~’没有参数和返回值

构造函数与析构函数

注:构造函数可能有多个,但析构函数只能有一个
作用

  1. 析构函数
    析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作
    析构函数对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
  2. 构造函数
    该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作
    构造函数的作用:初始化对象的数据成员。

特点
①. 在对象被创建时自动执行;
②. 构造函数的函数名与类名相同;
③. 没有返回值类型、也没有返回值;
④. 构造函数不能被显式调用

语法
构造函数语法:类名(){}

  • 构造函数,没有返回值也不写void
  • 函数名称与类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法: ~类名(){}

  • 析构函数,没有返回值也不写void
  • 函数名称与类名相同,在名称前加上符号~
  • 析构函数不可以有参数,因此不可以发生重载
  • 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
构造函数的种类
  • 实例构造函数
  • 静态构造函数
  • 私有构造函数
构造函数的显式定义
#include <iostream>

    using namespace std;

    class Point
    {
        public:
            Point()     //声明并定义构造函数
            {
                cout<<"自定义的构造函数被调用...\n";
                xPos = 100;         //利用构造函数对数据成员 xPos, yPos进行初始化
                yPos = 100;
            }
            void printPoint()
            {
                cout<<"xPos = " << xPos <<endl;
                cout<<"yPos = " << yPos <<endl;
            }

        private:
            int xPos;
            int yPos;
    };

    int main()
    {
        Point M;    //创建对象M
        M.printPoint();

        return 0;
    }
有参构造函数
#include <iostream>

    using namespace std;

    class Point
    {
        public:
            Point(int x = 0, int y = 0)     //带有默认参数的构造函数
            {
                cout<<"自定义的构造函数被调用...\n";
                xPos = x;         //利用传入的参数值对成员属性进行初始化
                yPos = y;
            }
            void printPoint()
            {
                cout<<"xPos = " << xPos <<endl;
                cout<<"yPos = " << yPos <<endl;
            }

        private:
            int xPos;
            int yPos;
    };

    int main()
    {
        Point M(10, 20);    //创建对象M并初始化xPos,yPos为10和20
        M.printPoint();

        Point N(200);       //创建对象N并初始化xPos为200, yPos使用参数y的默认值0
        N.printPoint();

        Point P;            //创建对象P使用构造函数的默认参数
        P.printPoint();

        return 0;
    }
构造函数的重载
Point(int x = 0, int y = 0)     //默认参数的构造函数
        {
            xPos = x;
            yPos = y;
        }

        Point()         //重载一个无参构造函数
        {
            xPos = 0;
            yPos = 0;
        }

1)实例构造函数

  • 构造函数的名字和类名相同
  • 有用new表达式创建类的对象或者结构的时,会调用其构造函数,并通常初始化新对象的数据成员
  • 除非类是静态的,否则会为没有构造函数的类,自动生成一个默认构造函数
  • 构造函数有参数可以以多态的形式存在多个构造函数

2)静态构造函数

  • 静态构造函数不使用访问修饰符或不具有参数。
  • 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类。
  • 不能直接调用静态构造函数。
  • 如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在程序运行所在的应用程序域的生存期内将保持未初始化。

3)私有构造函数

  • 一种特殊的实例构造函数。 它通常用于只包含静态成员的类中。 如果类具有一个或多个私有构造函数而没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例

创建某个类型的第一个实例时,所进行的操作顺序为:
(1)静态变量设置为0
(2)执行静态变量初始化器
(3)执行基类的静态构造函数
(4)执行静态构造函数
(5)实例变量设置为0
(6)执行衯变量初始化器
(7)执行基类中合适的实例构造函数
(8)执行实例构造函数
注:同样类型的第二个以及以后的实例将从第5步开始执行,因为类的构造器仅会执行一次。此外,第6步和第7步将被优化,以便构造函数初始化器使编译器移除重复的指令。

拷贝构造函数

拷贝构造函数用于从一个已存在的对象创建一个新的对象,即复制构造函数
拷贝构造函数的参数通常是 const 引用类型的对象:
MyClass(const MyClass& other);

析构函数
析构函数
class A
{
private :
    char * p;
public:
    A ( )
    {
        p = new char[10];
    }
    ~ A ( )
    {
        delete [] p;
    }
};

调用析构函数的几种情况

  • 如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
  • static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
  • 如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
  • 如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
  • 调用复制构造函数后。

静态成员

类对象为静态
注:静态成员不属于对象而是属于类
就像变量一样,对象也在声明为静态时具有范围,直到程序的生命周期。

非静态成员的生命周期
#include<iostream> 
using namespace std; 

class Apple 
{ 
	int i; 
	public: 
		Apple() 
		{ 
			i = 0; 
			cout << "Inside Constructor\n"; 
		} 
		~Apple() 
		{ 
			cout << "Inside Destructor\n"; 
		} 
}; 

int main() 
{ 
	int x = 0; 
	if (x==0) 
	{ 
		Apple obj; 
	} 
	cout << "End of main\n"; 
} 

输出结果
Inside Constructor
Inside Destructor
End of main
静态成员生命周期
#include<iostream> 
using namespace std; 

class Apple 
{ 
	int i; 
	public: 
		Apple() 
		{ 
			i = 0; 
			cout << "Inside Constructor\n"; 
		} 
		~Apple() 
		{ 
			cout << "Inside Destructor\n"; 
		} 
}; 

int main() 
{ 
	int x = 0; 
	if (x==0) 
	{ 
		static Apple obj; 
	} 
	cout << "End of main\n"; 
} 

输出结果
Inside Constructor
End of main
Inside Destructor

静态成员函数

就像类中的静态数据成员或静态变量一样,静态成员函数也不依赖于类的对象。我们被允许使用对象和'.'来调用静态成员函数。但建议使用类名和范围解析运算符调用静态成员。
允许静态成员函数仅访问静态数据成员或其他静态成员函数,它们无法访问类的非静态数据成员或成员函数。
1、静态成员函数类似于静态成员变量都属于类而不是对象。
2、静态成员函数仅可以调用类的静态成员变量,不可以调用普通成员变量。
3、不具有this指针,因而自然不能声明为const。
4、如果类的成员函数想作为回调函数来使用,一般情况下只能将它定义为静态成员才行。

访问静态成员函数
#include<iostream> 
using namespace std; 

class Apple 
{ 
    public: 
        // static member function 
        static void printMsg() 
        {
            cout<<"Welcome to Apple!"; 
        }
}; 

// main function 
int main() 
{ 
    // invoking a static member function 
    Apple::printMsg(); 
} 

虚函数

虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载
纯虚函数的声明有着特殊的语法格式:virtual 返回值类型成员函数名(参数表)=0;
构造函数不可以是虚函数,析构函数可以是虚析构函数
虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。

纯虚函数

纯虚函数是在基类中声明的虚函数,在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
基类中实现纯虚函数的方法就是在函数的原型后面加“=0”
引入原因
通俗解释:动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理
含纯虚函数的类称为抽象类:被继承而不能直接创建对象的类
抽象类是不能定义对象的,在实际中为了强调一个类是抽象类,可将该类的构造函数说明为保护的访问控制权限。
相关规定:

  1. 抽象类只能用作其他类的基类,不能建立抽象类对象
  2. 抽象类不能用作参数类型、函数返回类型或显式转换的类型。
  3. 可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。
纯虚函数解释
/**
 * @brief
 * 抽象类中:在成员函数内可以调用纯虚函数,在构造函数/析构函数内部不能使用纯虚函数
 * 如果一个类从抽象类派生而来,它必须实现了基类中的所有纯虚函数,才能成为非抽象类
 * @date 2024-07-12
 */
#include <iostream>
using namespace std;

class A {
public:
  virtual void f() = 0; // 纯虚函数
  void g() { this->f(); }
  A() {}
};
class B : public A {
public:
  void f() { cout << "B:f()" << endl; }
};
int main() {
  B b;
  b.g();
  return 0;
}

注释

  1. 纯虚函数中的对象没有实际功能,通过派生类进行实现。A中的f函数(纯虚函数)没有实际意义,通过派生类B中进行函数功能实现。
  2. 如果一个类从抽象类派生而来,它必须实现了基类中的所有纯虚函数,才能成为非抽象类。
posted @ 2024-07-12 10:27  不吃番茄味的崽  阅读(29)  评论(0)    收藏  举报