资讯动态

c++ --类和对象【类的定义,初始化,构造函数】

发布时间:2026/8/29 12:45:26 来源:尧图企业网站定制
目录1.类的概念2.类的定义3.对象的创建:3.1. 栈上创建自动存储3.2. 堆上创建动态存储3.3. 静态创建3.4 常量对象4.封装5.继承6. 类构造函数 析构函数6.1 构造函数6.2使用初始化列表初始化字段6.3 析构函数6.3.1 构造函数先父类后子类6.3.2. 析构函数先子类后父类6.3.3 为什么析构反过来7 虚函数与析构函数的探讨1.类的概念类用于指定对象的形式是一种用户自定义的数据类型它是一种封装了数据和函数的组合。类中的数据称为成员变量函数称为成员函数。类可以被看作是一种模板可以用来创建具有相同属性和行为的多个对象。2.类的定义在 C 中类的定义通常包括以下几个部分数据成员成员变量用于存储对象的状态或属性。成员函数用于定义对象的行为或功能。#include iostream #include string class Dog { public: // 数据成员 std::string name; int age; // 成员函数 void bark() { std::cout Woof! My name is name and I am age years old. std::endl; } };3.对象的创建:使用类定义后可以创建类的对象。每个对象都是类的一个实例拥有自己的数据成员值。int main() { Dog myDog; // 创建对象 myDog myDog.name Buddy; // 设置属性 myDog.age 3; // 设置属性 myDog.bark(); // 调用成员函数 return 0; }这里多提一下在 C 中创建对象的方式主要有三种栈上创建自动存储、堆上创建动态存储和静态创建。以下是每种方式的示例3.1. 栈上创建自动存储在栈上创建对象生命周期由作用域决定。当对象超出作用域时自动调用析构函数释放内存。#include iostream using namespace std; class MyClass { public: MyClass() { cout Constructor called. endl; } ~MyClass() { cout Destructor called. endl; } void display() { cout Hello from MyClass! endl; } }; int main() { MyClass obj; // 在栈上创建对象 obj.display(); return 0; }3.2. 堆上创建动态存储使用new运算符在堆上创建对象必须手动释放内存。#include iostream using namespace std; class MyClass { public: MyClass() { cout Constructor called. endl; } ~MyClass() { cout Destructor called. endl; } void display() { cout Hello from MyClass! endl; } }; int main() { MyClass* obj new MyClass(); // 在堆上创建对象 obj-display(); delete obj; // 手动释放内存 return 0; }3.3. 静态创建静态创建对象通常在全局范围或静态作用域内进行生命周期从程序开始到程序结束。#include iostream using namespace std; class MyClass { public: MyClass() { cout Constructor called. endl; } ~MyClass() { cout Destructor called. endl; } void display() { cout Hello from MyClass! endl; } }; // 全局静态对象 MyClass globalObj; int main() { globalObj.display(); return 0; }3.4 常量对象不可修改常量对象的状态不能被修改。你无法通过常量对象调用会改变其成员变量的非const成员函数。例如如果你有一个BankAccount类的常量对象调用deposit()或withdraw()方法会导致编译错误。只能调用const成员函数常量对象只能调用标记为const的成员函数。这样的成员函数承诺不改变对象的状态。class BankAccount { private: double balance; public: BankAccount(double initialBalance) : balance(initialBalance) {} void deposit(double amount) { balance amount; // 非 const 方法 } double getBalance() const { return balance; // const 方法 } }; int main() { const BankAccount account(100.0); // 创建常量对象 // account.deposit(50.0); // 错误无法调用非 const 方法 double balance account.getBalance(); // 正确可以调用 const 方法 return 0; }4.封装封装是面向对象编程的一个重要特性它通过控制访问权限来保护对象的状态。使用访问修饰符可以控制类的成员是否可以被外部访问。访问修饰符public可以被外部代码访问。private只能被类内部访问默认情况下是定义为 private。protected可以被类内部和派生类访问。class BankAccount { private: double balance; // 私有成员不能直接访问 public: // 构造函数 BankAccount(double initialBalance) : balance(initialBalance) {} // 公共成员函数 void deposit(double amount) { if (amount 0) { balance amount; } } double getBalance() { return balance; // 允许外部访问余额 } }; int main() { BankAccount account(100.0); // 创建 BankAccount 对象 account.deposit(50.0); // 存款 std::cout Balance: account.getBalance() std::endl; // 输出余额 // account.balance 200; // 错误不能直接访问私有成员 return 0; }5.继承继承允许创建新类派生类基于现有类基类扩展其功能。派生类可以继承基类的属性和方法并可以添加新的属性和方法。class Animal { public: void eat() { std::cout Eating... std::endl; } }; // 派生类 class Cat : public Animal { public: void meow() { std::cout Meow! std::endl; } }; int main() { Cat myCat; // 创建 Cat 对象 myCat.eat(); // 调用基类方法 myCat.meow(); // 调用派生类方法 return 0; }Cat类继承了Animal类因此myCat对象可以调用Animal类中的eat方法。6. 类构造函数 析构函数6.1 构造函数构造函数是类中的一种特殊成员函数用于在创建对象时初始化对象的状态。构造函数也可以带参数。class Example { public: Example() { // 默认构造函数 // 构造函数体 } Example(int x) { // 带参数的构造函数 // 构造函数体 } };构造函数的特性名称相同构造函数的名称与类的名称完全相同。无返回值构造函数没有返回类型连void也不返回。自动调用构造函数在创建对象时自动调用无需手动调用。重载构造函数可以被重载允许不同数量或类型的参数。初始化成员变量构造函数常用于初始化对象的成员变量。下面是代码示例#include iostream #include string class Person { private: std::string name; int age; public: // 默认构造函数构造函数的名称与类名相同 Person() { name 未知; ” age 0; } // 带参数的构造函数允许不同数量或类型的参数构造函数重载 Person(std::string n, int a) { name n; age a; } // 成员函数用于显示对象的姓名和年龄 void display() { std::cout 姓名: name , 年龄: age std::endl; // 输出姓名和年龄 } }; int main() { // 使用默认构造函数创建对象自动调用构造函数 Person person1; person1.display(); // 输出: 姓名: 未知, 年龄: 0 // 使用带参数的构造函数创建对象自动调用构造函数 Person person2(Alice, 30); person2.display(); // 输出: 姓名: Alice, 年龄: 30 return 0; // }6.2使用初始化列表初始化字段使用初始化列表来初始化类成员变量是 C 中一种有效的方式。主要用于初始化成员变量初始化列表可以提高性能尤其是对于复杂对象因为它们在创建对象时直接初始化而不是在构造函数体内赋值。class Example { private: int a; const int b; // 常量成员 public: Example(int x, int y) : a(x), b(y) { // 初始化列表 // 构造函数体 } };#include iostream // class Line { private: double length; // 私有成员变量表示线段的长度 public: // 带参数的构造函数使用初始化列表初始化 length Line(double len) : length(len) { // 初始化列表直接初始化 length std::cout Object is being created, length len std::endl; // 输出创建信息 } }; class C { private: double X; // 私有成员变量 X double Y; // 私有成员变量 Y double Z; // 私有成员变量 Z public: // 带参数的构造函数使用初始化列表初始化 X, Y, Z C(double a, double b, double c) : X(a), Y(b), Z(c) { // 初始化列表直接初始化 X, Y, Z std::cout Object C created with X X , Y Y , Z Z std::endl; // 输出创建信息 } }; int main() { // 创建 Line 对象自动调用构造函数 Line line1(5.0); // 输出: Object is being created, length 5.0 // 创建 C 对象自动调用构造函数 C obj(1.0, 2.0, 3.0); // 输出: Object C created with X 1, Y 2, Z 3 return 0; // }6.3 析构函数类的析构函数是类的一种特殊的成员函数它会在每次删除所创建的对象时执行。析构函数的特点名称析构函数的名称与类名相同但前面加上波浪号 ~。无返回值和参数析构函数不会返回任何值且不接受任何参数。自动调用在对象生命周期结束如超出作用域、删除对象时析构函数自动调用用于清理资源例如释放内存、关闭文件等。示例在 main 函数中创建的 person1 和 person2 对象在超出作用域时其析构函数会被自动调用输出释放资源的消息。#include iostream #include string class Person { private: std::string name; int age; public: // 默认构造函数构造函数的名称与类名相同 Person() { name 未知; // 初始化姓名为“未知” age 0; // 初始化年龄为0 } // 带参数的构造函数允许不同数量或类型的参数构造函数重载 Person(std::string n, int a) { name n; // 将参数 n 赋值给成员变量 name age a; // 将参数 a 赋值给成员变量 age } // 析构函数析构函数的名称与类名相同前面加波浪号~ ~Person() { // 析构函数在对象生命周期结束时自动调用 std::cout 析构函数被调用释放资源: name std::endl; // 输出析构信息 } // 成员函数用于显示对象的姓名和年龄 void display() { std::cout 姓名: name , 年龄: age std::endl; // 输出姓名和年龄 } }; int main() { // 使用默认构造函数创建对象自动调用构造函数 Person person1; person1.display(); // 输出: 姓名: 未知, 年龄: 0 // 使用带参数的构造函数创建对象自动调用构造函数 Person person2(Alice, 30); person2.display(); // 输出: 姓名: Alice, 年龄: 30 // person2 对象超出作用域后自动调用析构函数 return 0; // }6.3.1 构造函数先父类后子类当你创建一个子类对象class Parent { public: Parent() { cout 父类构造\n; } }; class Child : public Parent { public: Child() { cout 子类构造\n; } };执行Child c;顺序是① Parent 构造函数 ↓ ② Child 构造函数输出父类构造 子类构造因为Child 对象里面包含一个 Parent 部分。可以粗略理解成Child 对象 ┌─────────────────┐ │ Parent 部分 │ ← 必须先构造好 ├─────────────────┤ │ Child 自己的部分 │ ← 然后才能构造 └─────────────────┘子类构造函数开始执行之前父类那一部分必须先初始化完成。6.3.2. 析构函数先子类后父类当Child c;离开作用域时① Child 析构函数 ↓ ② Parent 析构函数也就是构造父 → 子 析构子 → 父6.3.3 为什么析构反过来因为子类可能依赖父类提供的东西。所以销毁时必须先清理 Child 自己的东西 ↓ 再清理 Parent 的东西例如Child ├── Child自己的资源 └── Parent的资源销毁这样才安全先释放 Child资源 ↓ 再释放 Parent资源7 虚函数与析构函数的探讨析构函数是否是虚函数取决于是否有继承关系及多态的需求。虚析构函数在基类中声明虚析构函数后当通过基类指针删除派生类对象时C会确保正确调用派生类的析构函数然后再调用基类的析构函数。这是多态的一部分确保派生类的资源能够被正确释放。当一个类有派生类并且使用基类指针或引用来删除对象时基类的析构函数必须是虚函数否则将发生资源泄漏或者未定义行为。具体来说如果基类的析构函数不是虚函数删除通过基类指针指向派生类对象时只会调用基类的析构函数而不会调用派生类的析构函数导致派生类的资源没有被正确释放从而产生内存泄漏。#include iostream class Base { public: // 虚析构函数 virtual ~Base() { std::cout Base Destructor std::endl; } }; class Derived : public Base { public: ~Derived() override { std::cout Derived Destructor std::endl; } }; int main() { // 创建一个派生类对象但通过基类指针来删除它 Base* obj new Derived(); delete obj; // 正确调用派生类和基类的析构函数 return 0; }总结虚析构函数当有继承关系时尤其是当基类指针或引用指向派生类对象时基类的析构函数应当是虚函数以确保删除派生类对象时能够正确调用派生类和基类的析构函数。非虚析构函数如果没有继承或多态的需求析构函数可以是非虚的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价