目录
类的封装
语法格式
声明定义
分文件
访问权限
类作用域
对象模型
构造函数
默认构造函数
带参构造函数
拷贝构造函数
构造函数重载
委托构造函数
初始数据列表
构造默认参数
构造函数删除
析构函数
析构函数概念
析构函数特性
析构函数示例
析构调用顺序
析构调用时机
浅拷贝深拷贝
静态成员
静态变量
静态函数
静态特性
静态特性
常量成员
const成员变量
const成员函数
const成员函数重载机制
const类的对象
对象指针
class classname
{
//默认属性
private:
//成员变量
//成员函数
};
类主体
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
#include
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
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
//全局变量
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
//空类
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; //1
std::cout << sizeof(c2) << std::endl; //1
std::cout << sizeof(c3) << std::endl; //1
std::cout << sizeof(c4) << std::endl; //4
std::cout << sizeof(c5) << std::endl; //4
return 0;
}
对齐方式:为了提高内存访问的效率,编译器通常会对成员变量进行对齐。对齐规则可以通过编译选项进行配置,也可以使用特定的对齐指令来修改。
#include
class c1
{
char m_c; //1
short m_s; //2
int m_i; //4
long long m_ll; //8
//15
};
int main()
{
std::cout << sizeof(c1) << std::endl; //16
return 0;
}
访问权限:成员变量的访问权限(公共、私有等)不会影响对象内存结构布局,所有成员变量都按照声明顺序存储。
#include
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
class MyClass
{
public:
// 默认构造函数
MyClass()
{
std::cout << "默认构造函数被调用" << std::endl;
}
};
int main()
{
// 创建对象时调用默认构造函数
MyClass obj;
return 0;
}
#include
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
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
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
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
class MyClass
{
public:
int value;
// 构造函数使用初始化列表
MyClass() : value(10)
{
std::cout << "构造函数被调用,value = " << value << std::endl;
}
};
int main()
{
// 创建对象时调用带参数的构造函数
MyClass obj;
return 0;
}
#include
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 = 0
MyClass obj2(10); // value1 = 10, value2 = 0
MyClass obj3(20, 30); // value1 = 20, value2 = 30
return 0;
}
删除构造函数将阻止该构造函数的调用,从而禁止使用特定的构造方式。
#include
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;
}
析构函数通常用于释放在对象生命周期期间分配的资源,如堆上的内存、打开的文件、网络连接等。
#include
class MyClass
{
public:
// 构造函数
MyClass()
{
std::cout << "构造函数被调用" << std::endl;
}
// 析构函数
~MyClass()
{
std::cout << "析构函数被调用" << std::endl;
}
};
int main()
{
{
MyClass obj; // 创建一个对象
} // 对象超出作用域,析构函数被调用
return 0;
}
#include
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;
}
#include
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
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
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;
}
#include
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
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
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;
}
#include
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; //4
return 0;
}
在类中,可以将成员函数声明为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成员变量
};
class MyClass
{
public:
void func()
{
// 非const成员函数
}
void func() const
{
// const成员函数
}
};
const成员函数的使用场景
#include
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 MyClass obj;
obj.func(); // 合法,调用const成员函数
// obj.nonConstFunc(); // 错误,不能调用非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指针特性
#include
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指针
#include
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;
}