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.rhkb.cn/news/248910.html

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

相关文章

AIGC项目——Meta:根据对话音频生成带动作和手势的3d逼真数字人

From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations From Audio to Photoreal Embodiment:Synthesizing Humans in Conversations 从二元对话的音频中&#xff0c;我们生成相应的逼真的面部、身体和手势。 概括性:角色是由作者的声音驱动的(而不是模…

【解决视网膜长尾数据】实例级类平衡、层次预训练、混合知识蒸馏

解决视网膜长尾数据&#xff1a;实例级类平衡、层次预训练、混合知识蒸馏 问题&#xff1a;视网膜疾病分类&#xff0c;解法&#xff1a;深度学习模型问题&#xff1a;数据复杂性处理&#xff0c;解法&#xff1a;多任务框架&#xff08;同时处理多种疾病&#xff09;和少量样本…

TSINGSEE青犀视频智慧电梯管理平台,执行精准管理、提升乘梯安全

一、方案背景 随着城市化进程的不断加快&#xff0c;我国已经成为全球最大的电梯生产和消费市场&#xff0c;电梯也成为人们日常生活中不可或缺的一部分。随着电梯数量的激增&#xff0c;电梯老龄化&#xff0c;维保数据不透明&#xff0c;物业管理成本高&#xff0c;政府监管…

C#: form 添加窗体最小化事件,添加系统托盘图标,点击后可以打开、最小软件窗口

说明&#xff1a; 1.实现窗体在最小化后触发一个事件&#xff0c;可以去实现需要的功能。 2.最小化后软件图标出现在系统右下角的托盘串口。 3.点击托盘口的图标可以实现软件弹出窗口和最小化的切换。 1.参考办法 以下是判断C#窗体最小化到状态栏的状态的方法&#xff1a;…

javaweb项目,springboot幼儿园健康管理系统,界面美观,增删改查。

javaweb项目&#xff0c;幼儿园健康管理系统&#xff0c;界面美观&#xff0c;增删改查。 管理员&#xff0c;老师&#xff0c;学生三个角色。 功能&#xff1a;权限管理&#xff0c;咨询列表&#xff0c;教师列表&#xff0c;班级列表&#xff0c;健康档案列表&#xff0c;评…

C++——特殊类

特殊类 文章目录 特殊类一、请设计一个类&#xff0c;不能被拷贝二、请设计一个类&#xff0c;只能在堆上创建对象方案一&#xff1a;析构函数私有化方案二&#xff1a;构造函数私有化 三、请设计一个类&#xff0c;只能在栈上创建对象四、请设计一个类&#xff0c;不能被继承五…

c++连接mysql

c连接mysql 安装mysql以及c对应的库进入数据库&#xff0c;创建数据库&#xff0c;表&#xff0c;并新建管理员用户编写c代码编译运行&#xff0c;测试结果头文件解释 安装mysql以及c对应的库 sudo apt-get update sudo apt-get install mysql-server sudo apt-get install li…

CISAW和CISP-PTE证书选择指南

&#x1f4e3;在信息安全领域&#xff0c;选择合适的证书可以为你的职业生涯增添光彩。很多从事信息渗透行业的朋友经常讨论CISP-PTE和CISAW之间的选择问题。今天就从4个方面带你详细了解这两张证书&#xff0c;帮你做出明智的选择&#xff01; 1️⃣证书的行业前景 &#x1f4…

python爬虫爬取网站

流程&#xff1a; 1.指定url(获取网页的内容) 爬虫会向指定的URL发送HTTP请求&#xff0c;获取网页的HTML代码&#xff0c;然后解析HTML代码&#xff0c;提取出需要的信息&#xff0c;如文本、图片、链接等。爬虫请求URL的过程中&#xff0c;还可以设置请求头、请求参数、请求…

Ubuntu系统安装 Redis

环境准备 Ubuntu 系统版本&#xff1a;22.04.3Redis 版本&#xff1a;6.2.12 检查本地 make 环境 make -version若没有安装&#xff0c;则需要安装 sudo apt install make检查本地 gcc 环境 gcc -version若没有安装&#xff0c;则需要安装 sudo apt install gcc。 sudo a…

Single-Head ViT;Faster Whisper;Transformer KF;Pick-and-Draw

本文首发于公众号&#xff1a;机器感知 Single-Head ViT&#xff1b;Faster Whisper&#xff1b;Transformer KF&#xff1b;Pick-and-Draw SHViT: Single-Head Vision Transformer with Memory Efficient Macro Design Recently, efficient Vision Transformers have shown …

【遥感专题系列】遥感影像信息提取之——人工目视解译

​遥感影像通过亮度值或像元值的高低差异&#xff08;反映地物的光谱信息&#xff09;及空间变化&#xff08;反映地物的空间信息&#xff09;来表示不同地物的差异&#xff0c;这是区分不同影像地物的物理基础。 ​人工解译是目前国内使用最多的一种影像提取方法&#xff0c;如…

算法练习-螺旋矩阵(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;数组 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。以下内容均为个人笔记&#xff0c;旨在督促自己认真学习。 题目 给定一个正整数n&#xff0c;生成一个包含1到 n^2 所有元…

API网关-Apisix RPM包方式自动化安装配置教程

文章目录 前言一、简介1. etcd简介2. APISIX简介3. apisix-dashboard简介 二、Apisix安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. 浏览器访问5. 卸载Apisix 三、命令1. Apisix命令1.1 启动apisix服务1.2 停止apisix服务1.3 优雅地停止apisix服务1.4 重启apisix服务1.5 重…

Android矩阵Matrix变换setRectToRect,Kotlin

Android矩阵Matrix变换setRectToRect&#xff0c;Kotlin 在 Android画布Canvas裁剪区域clipRect&#xff0c;Kotlin-CSDN博客 基础上&#xff0c;增加一个点&#xff0c;通过setRectToRect挖出Bitmap原图中心区域的一块放到目标RectF里面。 import android.content.Context imp…

Google Chrome 常用的几个参数

1 右键--Google Chrome--属性--目标 参数作用--disable-infobars此计算机将不会再收到 Google Chrome 更新&#xff0c;因为 Windows XP 和 Windows Vista 不再受支持。适用于 xp、2003 的 49.x.x.x 版本。示例1--ingore-certificate-errors忽略证书错误--disable-background-…

知识库建设这些工具来帮忙,企业工作效率翻倍

在当今深度信息化的年代&#xff0c;知识库成了企业不可或缺的一部分&#xff0c;它的建设与管理显得格外重要。然而&#xff0c;想要建设又好又高效的知识库并非易事。好消息是&#xff0c;有很多优秀的工具可以让这个过程变得更加轻松&#xff0c;今天我们就重点来探讨其中的…

excel中去掉单元格中两个数字之间的空格

excel中去掉单元格中两个数字之间的空格 使用公式&#xff1a;SUBSTITUTE(A1," “,”") 解释&#xff1a;将A1单元格中的空格查找出来并去掉。

搭建WebGL开发环境

前言 本篇文章介绍如何搭建WebGL开发环境 WebGL WebGL的技术规范继承自免费和开源的OpenGL ES标准&#xff0c;从某种意义上说&#xff0c;WebGL就是Web版的OpenGL ES&#xff0c;而OpenGL ES是从OpenGL中派生出来的。他们的应用环境有区别&#xff0c;一般来说&#xff1a;…

C++:第十四讲动态规划初步

每日C知识 想要在做C小游戏里实现等待效果&#xff0c;可以用Sleep。 Sleep函数可以使计算机程序&#xff08;进程&#xff0c;任务或线程&#xff09;进入休眠&#xff0c;使其在一段时间内处于非活动状态。 一般需要头文件windows.h。 注意"Sleep"首字母要大写…