C/C++ - 类的封装特性

目录

类的封装

语法格式

声明定义

分文件

访问权限

类作用域

对象模型

构造函数

默认构造函数

带参构造函数

拷贝构造函数

构造函数重载

委托构造函数

初始数据列表

构造默认参数

构造函数删除

析构函数

析构函数概念

析构函数特性

析构函数示例

析构调用顺序

析构调用时机

浅拷贝深拷贝

静态成员

静态变量

静态函数

静态特性

静态特性

常量成员

const成员变量

const成员函数

const成员函数重载机制

const类的对象

对象指针


类的封装

  • 语法格式

    • class classname
      {//默认属性private://成员变量//成员函数	
      };
      
    • ​class ​​​​- 关键字定义类
    • ​classname ​​​​- 遵守标识符命名规则
  • 声明定义

    • 类主体

      class Person
      {
      private:std::string m_Name;int m_Age;public:void SetAge(int nAge){m_Age = nAge;}int GetAge(){return m_Age;}};
      
    • 分文件

      • 头文件

        #pragma once
        #include <iostream>
        #include <string>class CPerson
        {//私有属性
        private://成员变量std::string m_Name;int m_Age;//公共属性
        public://成员函数void SetAge(int nAge);int GetAge();};
        
      • 源文件

        #include "CPerson.h"void CPerson::SetAge(int nAge)
        {m_Age = nAge;
        }int CPerson::GetAge()
        {return m_Age;
        }
        

  • 访问权限

    • ​public​​​​:公共成员在类内部和外部均可访问。它们对外部用户公开,可以自由访问。

    • ​private​​​​:私有成员仅在类内部可访问。它们对外部用户隐藏,只能通过类的公共成员函数进行访问。

    • ​protected​​​​:受保护成员在类内部可访问,也可以在派生类中访问。它们对外部用户隐藏,但可以被派生类继承并访问。

    • 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。

    • class的默认访问权限为private​​​​,struct为public​​​​。

    • 示例代码

      #include <iostream>class Person
      {//公共
      public:int m_Age;void SetAge(int nAge){m_Age = nAge;}int PublicGetMoney(){//类内部可以访问私有成员函数return GetMoney();}//保护
      protected://私有
      private:int m_Money;int GetMoney(){return m_Money;}};int main()
      {//实例化对象Person p1;//公共权限 -> 类外部可以访问p1.m_Age = 18;p1.SetAge(20);//私有属性 -> 类外部无法访问//p1.m_Money;p1.PublicGetMoney();return 0;
      }
      

  • 类作用域

    • 类作用域:类作用域是指在类的定义内部声明的成员,在整个类中可见。

    • 对象作用域:对象作用域是指在类的对象中,通过对象名访问的成员。对象作用域仅限于该对象。

    • 类名作用域:类名作用域是指在类的外部,使用类名和作用域解析运算符(::)访问的成员。

    • 代码示例

      #include <iostream>//全局变量
      int a = 20;namespace CC_STD
      {int a = 30;
      }class Person
      {
      public:int m_Age;int GetAge();static int Ver;private:void SetAge(int Age){m_Age = Age;}
      };int Person::GetAge()
      {return m_Age;
      }int Person::Ver = 0;int main()
      {int a = 10;//局部变量std::cout << a << std::endl;//全局变量std::cout << ::a << std::endl;//命名空间std::cout << CC_STD::a << std::endl;//类实例化Person p1;p1.m_Age = 18;//类名输出std::cout << Person::Ver << std::endl;return 0;
      }
      

  • 对象模型

    • C++类的对象内存结构布局描述了类对象在内存中的存储方式。了解对象内存结构布局有助于理解类对象的成员变量和成员函数在内存中的位置和访问方式。

      • 成员变量的存储:类的成员变量按照声明的顺序存储在对象内存中。每个成员变量占据一定的内存空间,根据数据类型的大小而定。

        #include <iostream>//空类
        class c1
        {};//成员函数 = 1
        class c2
        {void Fun1() {};
        };//成员函数 = 2
        class c3
        {void Fun1() {};void Fun2() {};
        };//成员变量 = int
        class c4
        {int Num;
        };//成员变量 = int
        //成员函数 = 1
        class c5
        {int Num;void Fun1() {};
        };int main()
        {std::cout << sizeof(c1) << std::endl;	//1std::cout << sizeof(c2) << std::endl;	//1std::cout << sizeof(c3) << std::endl;	//1std::cout << sizeof(c4) << std::endl;	//4std::cout << sizeof(c5) << std::endl;	//4return 0;
        }
        
      • 对齐方式:为了提高内存访问的效率,编译器通常会对成员变量进行对齐。对齐规则可以通过编译选项进行配置,也可以使用特定的对齐指令来修改。

        #include <iostream>class c1
        {char m_c;			//1short m_s;			//2int m_i;			//4long long m_ll;		//8//15
        };int main()
        {std::cout << sizeof(c1) << std::endl;	//16return 0;
        }
        

      • 访问权限:成员变量的访问权限(公共、私有等)不会影响对象内存结构布局,所有成员变量都按照声明顺序存储。

        #include <iostream>class c1
        {
        public:void InitData(){m_A = 1;m_B = 2;m_C = 3;m_D = 4;}public:int m_A;private:int m_B;public:int m_C;private:int m_D;
        };int main()
        {c1 c;c.InitData();return 0;
        }
        

      • 虚函数表指针(vptr):虚函数表指针是一个指向虚函数表的指针,它存在于包含虚函数的类对象中。虚函数表是一个存储着虚函数地址的表格,使得派生类的虚函数能够覆盖基类的虚函数。虚函数表指针通常位于对象内存布局的开头或结尾,用于在运行时动态查找并调用正确的虚函数。

  • 构造函数

    • 默认构造函数

      • 当类定义中没有显式定义构造函数时,编译器会自动生成一个默认构造函数。
      • 默认构造函数是一个没有任何参数的构造函数。
      • 默认构造函数用于创建对象时进行初始化操作。
      //默认构造函数
      classname()
      {}
      
      #include <iostream>class MyClass
      {
      public:// 默认构造函数MyClass(){std::cout << "默认构造函数被调用" << std::endl;}
      };int main()
      {// 创建对象时调用默认构造函数MyClass obj;return 0;
      }
      
    • 带参构造函数

      • 带参数的构造函数可以接受参数,并用这些参数对对象进行初始化。
      • 通过在类定义中声明带参数的构造函数,可以自定义对象的初始化过程。
      #include <iostream>class MyClass 
      {
      public:int value;// 默认构造函数MyClass(){std::cout << "默认构造函数被调用,value = " << value << std::endl;}// 带参构造函数MyClass(int num) {value = num;std::cout << "带参构造函数被调用,value = " << value << std::endl;}
      };int main() 
      {// 创建对象时调用默认构造函数MyClass obj1;// 创建对象时调用带参构造函数MyClass obj2(10);return 0;
      }
      
    • 拷贝构造函数

      • 如果没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。
      • 拷贝构造函数用于创建一个新对象,并将其初始化为现有对象的副本。
      • 拷贝构造函数的参数是同类型的对象的引用。
      #include <iostream>class MyClass
      {
      public:int value;// 默认构造函数MyClass(){std::cout << "默认构造函数被调用" << std::endl;}// 带参构造函数MyClass(int num){value = num;std::cout << "带参构造函数被调用,value = " << value << std::endl;}// 复制构造函数MyClass(const MyClass& other){value = other.value;std::cout << "复制构造函数被调用,value = " << value << std::endl;}
      };int main()
      {// 创建对象时调用带参构造函数MyClass obj1(10);// 使用复制构造函数创建新对象MyClass obj2 = obj1;return 0;
      }
      
    • 构造函数重载

      • 类可以具有多个构造函数,这称为构造函数的重载。
      • 构造函数的重载允许使用不同的参数列表来创建对象。
      • 编译器根据提供的参数来确定应该调用哪个构造函数。
      #include <iostream>class MyClass 
      {
      public:int value1;int value2;// 构造函数重载MyClass() {value1 = 0;value2 = 0;std::cout << "默认构造函数被调用" << std::endl;}MyClass(int num) {value1 = num;value2 = 0;std::cout << "带一个参数的构造函数被调用,value1 = " << value1 << std::endl;}MyClass(int num1, int num2) {value1 = num1;value2 = num2;std::cout << "带两个参数的构造函数被调用,value1 = " << value1 << ", value2 = " << value2 << std::endl;}
      };int main() 
      {// 调用不同的构造函数MyClass obj1;MyClass obj2(10);MyClass obj3(20, 30);return 0;
      }
      
    • 委托构造函数

      • 委托构造函数允许一个构造函数调用同一个类的其他构造函数来完成对象的初始化。
      • 委托构造函数使用冒号(:)语法来调用其他构造函数。
      #include <iostream>class MyClass 
      {
      public:int value1;int value2;// 委托构造函数MyClass() : MyClass(0, 0) {std::cout << "默认构造函数被调用" << std::endl;}MyClass(int num) : MyClass(num, 0) {std::cout << "带一个参数的构造函数被调用,value1 = " << value1 << std::endl;}MyClass(int num1, int num2) {value1 = num1;value2 = num2;std::cout << "带两个参数的构造函数被调用,value1 = " << value1 << ", value2 = " << value2 << std::endl;}
      };int main() 
      {// 调用不同的构造函数MyClass obj1;MyClass obj2(10);MyClass obj3(20, 30);return 0;
      }
      
    • 初始数据列表

      • 初始化列表用于在构造函数中初始化类的成员变量。
      • 初始化列表使用冒号(:)后跟成员变量的初始化。
      • 初始化列表可以提供更高效的初始化方式,尤其是对于成员变量是常量或引用类型的情况。
      #include <iostream>class MyClass 
      {
      public:int value;// 构造函数使用初始化列表MyClass() : value(10) {std::cout << "构造函数被调用,value = " << value << std::endl;}
      };int main() 
      {// 创建对象时调用带参数的构造函数MyClass obj;return 0;
      }
      
    • 构造默认参数

      • 构造函数可以有默认参数值,这样在创建对象时可以省略对应参数的传递。
      • 默认参数值在构造函数声明中指定,而不是在定义中。
      #include <iostream>class MyClass 
      {
      public:int value1;int value2;// 带默认参数的构造函数MyClass(int num1 = 0, int num2 = 0) : value1(num1), value2(num2) {std::cout << "构造函数被调用,value1 = " << value1 << ", value2 = " << value2 << std::endl;}
      };int main() 
      {// 调用构造函数时省略参数MyClass obj1;           // value1 = 0, value2 = 0MyClass obj2(10);       // value1 = 10, value2 = 0MyClass obj3(20, 30);   // value1 = 20, value2 = 30return 0;
      }
      
    • 构造函数删除

      • 可以使用关键字 delete​​​ 在类中显式删除构造函数。
      • 删除构造函数将阻止该构造函数的调用,从而禁止使用特定的构造方式。

        #include <iostream>class MyClass 
        {
        public:// 删除默认构造函数MyClass() = delete;// 删除拷贝构造函数MyClass(const MyClass&) = delete;// 构造函数MyClass(int num) {std::cout << "构造函数被调用,num = " << num << std::endl;}
        };int main() 
        {// 无法调用已删除的构造函数//MyClass obj1;                       // 错误,无法调用已删除的默认构造函数//MyClass obj2(obj1);                 // 错误,无法调用已删除的拷贝构造函数MyClass obj4(10);                   // 正确,调用构造函数return 0;
        }
        

  • 析构函数

    • 析构函数概念

      • 析构函数是在对象销毁时自动调用的特殊成员函数。
      • 析构函数用于清理对象分配的资源、释放内存和执行其他必要的清理操作。
    • 析构函数特性

      • 析构函数的名称与类名相同,前面加上波浪号(~)作为前缀。
      • 析构函数没有返回类型,包括void。
      • 析构函数没有参数。
      • 析构函数不能被重载。
    • 析构函数示例

      • 析构函数通常用于释放在对象生命周期期间分配的资源,如堆上的内存、打开的文件、网络连接等。

        #include <iostream>class MyClass 
        {
        public:// 构造函数MyClass() {std::cout << "构造函数被调用" << std::endl;}// 析构函数~MyClass() {std::cout << "析构函数被调用" << std::endl;}
        };int main() 
        {{MyClass obj;  // 创建一个对象} // 对象超出作用域,析构函数被调用return 0;
        }
        
    • 析构调用顺序

      • 如果类继承了其他类,那么析构函数将按照构造函数的调用顺序相反的顺序被调用。
      • 先调用派生类的析构函数,然后调用基类的析构函数。
      #include <iostream>class Base 
      {
      public:Base() {std::cout << "Base的构造函数被调用" << std::endl;}~Base() {std::cout << "Base的析构函数被调用" << std::endl;}
      };class Derived : public Base 
      {
      public:Derived() {std::cout << "Derived的构造函数被调用" << std::endl;}~Derived() {std::cout << "Derived的析构函数被调用" << std::endl;}
      };int main() 
      {Derived obj;  // 创建一个派生类对象return 0;
      }
      
    • 析构调用时机

      • 对象的析构函数在以下情况下被自动调用:
      • 对象超出作用域。
      • 对象作为局部变量在函数执行完毕后销毁。
      • 对象动态分配内存后使用delete​​​释放。
      • 对象作为成员变量,其所属的对象被销毁时。
      #include <iostream>class Person
      {
      public:Person(const char* szName, int nAge){//年龄赋值m_Age = nAge;m_Name = NULL;//内存申请char* szBuffer = (char*)malloc(strlen(szName) + 1/*'/0'*/);if (szBuffer){memset(szBuffer, 0, strlen(szName) + 1);memcpy(szBuffer, szName, strlen(szName));m_Name = szBuffer;}}~Person(){if (m_Name){free(m_Name);m_Name = NULL;}}char* m_Name;int m_Age;
      };int main()
      {Person p1("0xCC", 18);return 0;
      }
      
    • 浅拷贝深拷贝

      • 浅拷贝
      • 深拷贝
      #include <iostream>class Person
      {
      public:Person(const char* szName, int nAge){//年龄赋值m_Age = nAge;m_Name = NULL;//内存申请char* szBuffer = (char*)malloc(strlen(szName) + 1/*'/0'*/);if (szBuffer){memset(szBuffer, 0, strlen(szName) + 1);memcpy(szBuffer, szName, strlen(szName));m_Name = szBuffer;}}Person(const Person& ref){m_Name = NULL;m_Age = ref.m_Age;if (ref.m_Name){//内存申请char* szBuffer = (char*)malloc(strlen(ref.m_Name) + 1/*'/0'*/);if (szBuffer){memset(szBuffer, 0, strlen(ref.m_Name) + 1);memcpy(szBuffer, ref.m_Name, strlen(ref.m_Name));m_Name = szBuffer;}}}~Person(){if (m_Name){free(m_Name);m_Name = NULL;}}char* m_Name;int m_Age;
      };int main()
      {Person p1("0xCC", 18);Person p2(p1);return 0;
      }
      

  • 静态成员

    • 静态变量

      • 静态变量是在类中声明的静态成员变量,它与类的任何对象都无关,只有一个副本。
      • 静态变量在类的所有对象之间共享,它们存储在静态存储区,直到程序结束才会被销毁。
      • 静态变量可以公开或私有,可以通过类名或对象访问,也可以在类外部初始化。
      • 静态变量的访问权限与其他成员变量相同,可以是公有、私有或保护。
      • 静态变量的声明通常放在类的声明中,但必须在类外部初始化。
      #include <iostream>class MyClass {
      public:static int count;  // 静态变量声明MyClass() {count++;  // 每次创建对象时,增加count的值}~MyClass() {count--;  // 每次销毁对象时,减少count的值}
      };int MyClass::count = 0;  // 静态变量初始化int main() {MyClass obj1;std::cout << "Count: " << MyClass::count << std::endl;MyClass obj2;std::cout << "Count: " << obj2.count << std::endl;std::cout << "Count: " << obj1.count << std::endl;return 0;
      }
      
    • 静态函数

      • 静态函数是与类相关联的函数,它们属于整个类而不是类的实例。
      • 静态函数在类的对象上调用,而不是特定对象上调用,因此它们不能访问非静态成员变量和非静态成员函数。
      • 静态函数可以通过类名或对象调用,但通常习惯通过类名调用,例如ClassName::staticFunction()​​​。
      • 静态函数的声明和定义都在类的声明内部,并用static​​​关键字标记。
      #include <iostream>class MathUtility {
      public:static int add(int a, int b) {return a + b;}static int multiply(int a, int b) {return a * b;}
      };int main() {int sum = MathUtility::add(3, 5);std::cout << "Sum: " << sum << std::endl;int product = MathUtility::multiply(4, 6);std::cout << "Product: " << product << std::endl;return 0;
      }
      
    • 静态特性

      • 静态成员函数无法访问非静态成员

        • 静态成员函数只能访问静态成员变量和静态成员函数,无法直接访问非静态成员变量和非静态成员函数。
        • 这是因为非静态成员是与类的实例相关联的,而静态成员函数是与类相关联的。
      • 静态成员的作用域

        • 静态成员的作用域范围限于类的定义域内,可以在类的任何成员函数中访问。
        • 静态成员变量和静态成员函数可以在类的外部通过类名进行访问。
        #include <iostream>class MyClass 
        {
        public:static int count;  // 静态成员变量声明int value;         // 非静态成员变量static void incrementCount() {count++;  // 静态成员函数可以访问静态成员变量// value++;  // 静态成员函数无法访问非静态成员变量}void print() {std::cout << "Value: " << value << std::endl;}static void printCount() {std::cout << "Count: " << count << std::endl;// print();  // 静态成员函数无法直接调用非静态成员函数}
        };int MyClass::count = 0;  // 静态成员变量初始化int main() 
        {MyClass obj1;obj1.value = 5;MyClass::incrementCount();  // 通过类名调用静态成员函数obj1.print();               // 通过对象调用非静态成员函数MyClass::printCount();      // 通过类名调用静态成员函数return 0;
        }
        
    • 静态特性

      • 静态成员变量可以作为类的全局变量使用:

        • 静态成员变量在类的作用域内可见,但它们的生命周期超出了类的对象。
        • 这意味着可以在类的外部访问和修改静态成员变量,就像访问全局变量一样。
        • 静态成员变量可以通过类名或对象进行访问,但推荐使用类名访问,以明确表达静态性质。
      • 静态成员可以用于共享信息:

        • 静态成员变量可以用于在类的所有对象之间共享数据。
        • 这在跟踪类的实例数、记录与类相关的全局状态等方面非常有用。
      • 静态成员可以用于实现工具函数:

        • 静态成员函数可以作为类的工具函数,与特定对象无关地执行某些操作。
        • 这些静态成员函数可以在没有创建类对象的情况下直接调用,提供了一种方便的方式来执行与类相关的操作。
      • 友元函数可以访问静态成员:

        • 如果将函数声明为类的友元函数,那么这个函数可以访问类的私有静态成员。
        • 这可以用来提供对类的私有静态成员的特殊访问权限。
        #include <iostream>class Circle 
        {
        private:static const double PI;  // 静态成员变量声明public:static double calculateArea(double radius) {return PI * radius * radius;  // 静态成员函数使用静态成员变量}static void printPI() {std::cout << "PI: " << PI << std::endl;}friend void setPI(double value);  // 声明友元函数
        };const double Circle::PI = 3.14159;  // 静态成员变量初始化void setPI(double value) 
        {Circle::PI = value;  // 友元函数可以访问类的私有静态成员
        }int main() 
        {double radius = 2.5;double area = Circle::calculateArea(radius);  // 通过类名调用静态成员函数std::cout << "Area: " << area << std::endl;Circle::printPI();  // 通过类名调用静态成员函数setPI(3.14);  // 调用友元函数设置静态成员变量的值Circle::printPI();return 0;
        }
        

  • 常量成员

    • const成员变量

      • 在类的声明中,通过在成员变量前添加const关键字来声明常量成员。
      • 常量成员必须在构造函数的初始化列表中进行初始化(或者直接赋值)。
      • 常量成员一旦初始化完成,其值将不能再修改。
      #include <iostream>class MyClass
      {
      public://成员变量int m_a;//静态变量static int m_b;//常量成员const int m_Ver;//构造函数MyClass() : m_Ver(1), m_a(0){}};int MyClass::m_b = 3;int main()
      {MyClass m1;std::cout << sizeof(m1) << std::endl; //4return 0;
      }
      
    • const成员函数

      • 在类中,可以将成员函数声明为const成员函数。const成员函数表示该函数不会修改类的成员变量。

        class MyClass 
        {
        public:void func() const {// 这是一个const成员函数// 不能修改类的成员变量}
        };
        
      • 在类中,可以使用mutable关键字修饰成员变量,使其可以在const成员函数中修改。

        class MyClass 
        {
        public:void func() const {mutableVar = 10;  // 在const成员函数中修改mutable成员变量}private:mutable int mutableVar; // mutable成员变量
        };
        
      • const成员函数重载机制

        • 一个类可以有多个同名的成员函数,其中一个是const成员函数,另一个是非const成员函数。它们被视为重载函数,根据调用对象的const属性来选择调用哪个函数。
        class MyClass 
        {
        public:void func() {// 非const成员函数}void func() const {// const成员函数}
        };
        
      • const成员函数的使用场景

        • 当你想确保对象在调用成员函数时不会被修改。
        • 当你想通过一个const对象来访问该对象的成员函数。
        • 当你想在const对象上调用成员函数时,以便在多线程环境中确保对象的线程安全性。
        #include <iostream>class Person
        {
        public:Person(const std::string& szName):m_Name(szName){}const std::string& GetName(){std::cout << "const std::string& GetName()" << std::endl;return m_Name;}const std::string& GetName() const{std::cout << "const std::string& GetName() const" << std::endl;return m_Name;}private:const std::string m_Name;
        };int main()
        {Person p1("0xCC");std::string szName = p1.GetName();std::cout << szName << std::endl;const Person p2("0xAA");szName = p2.GetName();std::cout << szName << std::endl;return 0;
        }
        
    • const类的对象

      • 当一个对象被声明为const对象时,只能调用其const成员函数,而不能调用非const成员函数。
      const MyClass obj;
      obj.func();  // 合法,调用const成员函数
      // obj.nonConstFunc();  // 错误,不能调用非const成员函数
      ‍‍‍```
      
      • 在一个const类中,所有的成员函数都被自动视为const成员函数。这意味着在const类中,所有的成员函数都不能修改类的成员变量。
      class ConstClass {
      public:void func() const {// const成员函数}void modify() {// 错误,不能修改const类的成员变量}
      };int main() {const ConstClass obj;  // const类对象obj.func();  // 合法,调用const成员函数// obj.modify();  // 错误,不能调用非const成员函数return 0;
      }
      

  • 对象指针

    • this指针作用

      • this指针的作用域局限于非静态成员函数内部,它在函数体内是可见的。
      • 在成员函数中,可以使用this指针来访问当前对象的成员变量和成员函数。
    • this指针用途

      • 用于区分成员变量和参数变量:
        当成员变量和成员函数的参数变量名相同时,可以使用this指针来明确指示访问的是成员变量。
      • 在成员函数中返回当前对象:
        this指针可以在成员函数中返回当前对象的指针,方便链式调用或其他操作。
    • this指针特性

      • this指针是一个隐含在每个非静态成员函数中的特殊指针,指向当前对象的地址。
      • this指针可以在类的成员函数中使用,用于访问当前对象的成员变量和成员函数。
      • 当调用一个成员函数时,编译器会自动将当前对象的地址作为this指针传递给函数。
      • this指针是一个常量指针,不允许修改指向的对象。
      #include <iostream>class MyClass 
      {
      private:int data;public:// 构造函数MyClass(int data) {this->data = data;  // 使用this指针访问成员变量}// 成员函数void printData() {std::cout << "Data: " << this->data << std::endl;  // 使用this指针访问成员变量}void setData(int data) {this->data = data;  // 使用this指针访问成员变量}// 返回this指针的成员函数MyClass* getObject() {return this;  // 返回当前对象的地址}
      };int main() 
      {MyClass obj1(10);  // 创建一个对象obj1.printData();  // 输出对象的成员变量obj1.setData(20);  // 修改对象的成员变量obj1.printData();  // 输出修改后的成员变量MyClass* ptr = obj1.getObject();  // 返回当前对象的地址ptr->printData();  // 输出对象的成员变量return 0;
      }
      
    • const成员函数和this指针

      • const成员函数表示该函数不会修改对象的状态。
      • 在const成员函数中,this指针的类型是const ClassName*​​,即指向常量对象的指针。
      • 在const成员函数中,只能调用其他const成员函数,不能调用非const成员函数。
      #include <iostream>class MyClass {
      private:int data;public:MyClass(int data) {this->data = data;}void printData() const {std::cout << "Data: " << this->data << std::endl;}void setData(int data) {this->data = data;}MyClass* getObject() {return this;}const MyClass* getConstObject() const {return this;}
      };int main() {MyClass obj1(10);obj1.printData();obj1.setData(20);obj1.printData();MyClass* ptr = obj1.getObject();ptr->printData();const MyClass obj2(30);const MyClass* constPtr = obj2.getConstObject();constPtr->printData();return 0;
      }
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

坚持刷题 | 平衡二叉树

文章目录 题目考察点代码实现实现总结对实现进一步改进扩展提问 坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天继续二叉树&#xff1a;平衡二叉树 题目 110.平衡二叉树 考察点 递归能力&#xff1a; 能否使用递归来解决问题。树的基本操作&#xff1a;能否正确地访…

DS:带头双向循环链表的实现(超详细!!)

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01;&#xff01; 博主的上篇文章介绍了链表&#xff0c;以及单链表的实现。 单链表的实现&#xff08;超详细&#xff01;&#xff01;&#xff09; 其实单链表的全称叫做不带头单向不循环链表&#xff0c;本文…

zabbix使用自动发现批量监控服务器

当有大量新增服务器需要监控时&#xff0c;为避免一台一台手动操作浪费人力&#xff0c;我们使用自动发现功能来进行操作&#xff1a; 以下以zabbix6.4.0版本为例 如下,点击自动发现&#xff0c;创建发现规则&#xff1a; 点击更新&#xff0c;保存&#xff0c;之后点告警---…

在 React 组件中使用 JSON 数据文件,怎么去读取请求数据呢?

要在 React 组件中使用 JSON 数据&#xff0c;有多种方法。 常用的有以下几种方法&#xff1a; 1、直接将 JSON 数据作为一个变量或常量引入组件中。 import jsonData from ./data.json;function MyComponent() {return (<div><h1>{jsonData.title}</h1>&…

node.js Redis SETNX命令实现分布式锁解决超卖/定时任务重复执行问题

Redis SETNX 特性 当然&#xff0c;让我们通过一个简单的例子&#xff0c;使用 Redis CLI&#xff08;命令行界面&#xff09;来模拟获取锁和释放锁的过程。 在此示例中&#xff0c;我将使用键“lock:tcaccount_[pk]”和“status:tcaccount_[pk]”分别表示锁定键和状态键。 获…

AAC解码算法原理

关于更多音视频开发内容&#xff0c;请参考专栏音视频开发 AAC&#xff08;Advanced Audio Coding&#xff09;是一种高级音频编码标准&#xff0c;它是一种十分流行的音频压缩格式&#xff0c;通常用于存储和传输音频数据。AAC提供了高音质和高压缩效率&#xff0c;广泛应用于…

Android源码设计模式解析与实战第2版笔记(四)

第三章 自由扩展你的项目–Builder 模式 Builder 模式的定义 将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 Builder 模式的使用场景 相同的方法&#xff0c;不同的执行顺序&#xff0c;产生不同的事件结果时 多个部件或零件&…

Android 基础技术——Handler

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于 Handler 为什么一个线程对应一个Looper&#xff1f; 核心&#xff1a;通过ThreadLocal保证 Looper.prepare的时候&#xff0c;ThreadLocal.get如果不空报异常&#xff1b;否则调用ThreadLocal.set,…

Cesium渲染白膜数据

async DrawBaiMoFun2() {// tiles 矩阵变换let changePostion = (tileSet, tx, ty, tz, rx, ry, rz, scale, center) => {if (!center) return;const m = Cesium.Transforms.eastNorthUpToFixedFrame(center);const surface =center ||Cesium.Cartesian3.fromRadians(cartog…

自动驾驶代客泊车AVP决策规划详细设计

背景 随着产品的不断迭代&#xff0c;外部停车场的铺开&#xff0c;PAVP车辆需要应对的场景将越来越复杂&#xff0c;因此整体算法泛化能力的提升显得尤为关键。为了打磨巡航规划的能力&#xff0c;算法架构应当设计的更为灵活&#xff0c;可以针对使用场景迁入更为先进有效的算…

【Linux】分区向左扩容的方法

文章目录 为什么是向左扩容操作前的备份方法&#xff1a;启动盘试用Ubuntu后进行操作 为什么是向左扩容 Linux向右扩容非常简单&#xff0c;无论是系统自带的disks工具还是apt安装的gparted工具&#xff0c;都有图像化的界面可以操作。但是&#xff0c;都不支持向左扩容。笔者…

01 Redis的特性+下载安装启动+Redis自动启动+客户端连接

1.1 NoSQL NoSQL&#xff08;“non-relational”&#xff0c; “Not Only SQL”&#xff09;&#xff0c;泛指非关系型的数据库。 键值存储数据库 &#xff1a; 就像 Map 一样的 key-value 对。如Redis文档数据库 &#xff1a; NoSQL 与关系型数据的结合&#xff0c;最像关系…

免费电视TV盒子软件,好用的免费电视盒子软件大全,免费电视盒子APP大全,2024最新整理

1、TVbox下载地址、影视接口、配置教程 下载地址 TVbox TVbox可用接口地址合集 注&#xff1a;接口均来源于互联网收集分享&#xff01;所有接口都是经过测试的&#xff0c;如果出现加载失败等情况&#xff0c;可能是因为接口针对的盒子有兼容问题&#xff0c;可以多试试几…

Linux中查看端口被哪个进程占用、进程调用的配置文件、目录等

1.查看被占用的端口的进程&#xff0c;netstat/ss -antulp | grep :端口号 2.通过上面的命令就可以列出&#xff0c;这个端口被哪些应用程序所占用&#xff0c;然后找到对应的进程PID https://img-blog.csdnimg.cn/c375eb2bed754426b373907acaa7346e.png 3.根据PID查询进程。…

isctf---web

圣杯战争 php反序列 ?payloadO:6:"summon":2:{s:5:"Saber";O:8:"artifact":2:{s:10:"excalibuer";O:7:"prepare":1:{s:7:"release";O:5:"saber":1:{s:6:"weapon";s:52:"php://filter…

Ubuntu系统中部署C++环境与Visual Studio Code软件

本文介绍在Linux Ubuntu操作系统下,配置Visual Studio Code软件与C++代码开发环境的方法。 在文章VMware虚拟机部署Linux Ubuntu系统的方法中,我们介绍了Linux Ubuntu操作系统的下载、安装方法;本文则基于前述基础,继续介绍在Linux Ubuntu操作系统中配置Visual Studio Code…

【GitHub项目推荐--游戏模拟器(switch)】【转载】

01 任天堂模拟器 yuzu 是 GitHub 上斩获 Star 最多的开源 Nintendo Switch 模拟器 &#xff0c;使用 C 编写&#xff0c;考虑到了可移植性&#xff0c;该模拟器包括 Windows 和 Linux 端。 如果你的 PC 满足必要的硬件要求&#xff0c;该模拟器就能够运行大多数商业游戏&…

Django实战

一、开发登录表单 def login_form(request):html = <html><body><form method="post">用户名:<input name = "username" type="text"></input></br>密码:<input name = "password" type = &q…

CSS 之 图片九宫格变幻效果

一、简介 ​ 本篇博客用于讲解如何实现图片九宫格变幻的样式效果&#xff0c;将图片分为九块填充在33的的九宫格子元素中&#xff0c;并结合grid、hover、transition等CSS属性&#xff0c;实现元素hover时&#xff0c;九宫格子元素合并为一张完整图片的动画效果。 ​ 为了简化…

SpringMVC 环境搭建入门

SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架&#xff0c;属于SpringFrameWork 的后续产品&#xff0c;已经融合在 Spring Web Flow 中。 SpringMVC 已经成为目前最主流的MVC框架之一&#xff0c;并且随着Spring3.0 的发布&#xff0c;全面…