c++类和对象(下)
一、再谈构造函数
我们在之前就已经了解了构造函数,初始化成员变量主要使用的是函数体内赋值。
1、其实,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值表达式。
例如:
2、每个成员变量在初始化成员列表中只能出现一次,,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
3、引用成员变量、const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则编译会报错。
4、C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
初始化列表总结:1、无论是否显示写初始化列表,每个构造函数都有初始化列表;
2、无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化。
举例如下:
#include <iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day):_year(year), _month(month)//初始化列表, _day(day){//_year = year;} int GetRet() const {return _ret;}
private://声明int _year=2000;//缺省值int _month=12;int _day;int _ret=20;};
int main()
{int xx;Date d1( 2025, 12, 22);cout << d1.GetRet() << endl;return 0;}
#include<iostream>
using namespace std;
class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};
class Date
{
public:Date():_month(2){cout << "Date()" << endl;}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
private:// 注意这⾥不是初始化,这⾥给的是缺省值,这个缺省值是给初始化列表的// 如果初始化列表没有显⽰初始化,默认就会⽤这个缺省值初始化int _year = 1;int _month = 1;int _day;Time _t = 1;const int _n = 1;int* _ptr = (int*)malloc(12);
};
int main()
{Date d1;d1.Print();return 0;
}
二、类型转换
在c语言中,我们学过的类型转换:
(1)整型之间(2)整型和浮点数之间(3)整型和指针之间(4)指针和指针之间
在c++中支持:内置类型-->类类型 类类型-->类类型
1、c++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
#include <iostream>
using namespace std;// 人民币金额类
class RMB {
private:int yuan; // 存储金额(单位:元)
public:// 单参数构造函数:接收int类型(内置类型),用于隐式转换RMB(int y) : yuan(y) {cout << "触发隐式转换:int(" << y << ") -> RMB 对象" << endl;}// 成员函数:显示金额void showMoney() {cout << "当前金额:" << yuan << " 元" << endl;}
};int main() {// 关键代码:直接用int值给RMB对象赋值,触发隐式转换RMB myMoney = 500; // 等价于 RMB myMoney(500),但这里是隐式转换myMoney.showMoney();// 另一种隐式转换场景:函数参数传递void printRMB(RMB m); // 声明函数printRMB(1000); // 直接传int值,隐式转为RMB对象return 0;
}// 定义函数:参数为RMB类型
void printRMB(RMB m) {cout << "函数接收的金额:";m.showMoney();
}
2、构造函数前面加上explicit就不再支持隐式类型转换
#include <iostream>
using namespace std;class RMB {
private:int yuan;
public:// 关键:构造函数前加 explicit,禁止隐式转换explicit RMB(int y) : yuan(y) {cout << "显式创建 RMB 对象,金额:" << y << " 元" << endl;}void showMoney() {cout << "当前金额:" << yuan << " 元" << endl;}
};// 函数:接收 RMB 类型参数
void printRMB(RMB m) {m.showMoney();
}int main() {// 1. 尝试隐式转换:编译报错!因为构造函数加了 explicit// RMB myMoney = 500; // 错误:无法从 int 隐式转换为 RMB// 2. 尝试函数参数隐式转换:同样编译报错// printRMB(1000); // 错误:无法将参数 1 从 int 转换为 RMB// 3. 仅支持显式转换:通过直接调用构造函数实现RMB myMoney(500); // 正确:显式创建对象myMoney.showMoney();// 4. 显式转换的另一种方式:使用 static_castprintRMB(static_cast<RMB>(1000)); // 正确:显式转换后传参return 0;
}
3、类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
#include <iostream>
using namespace std;// 源类:手机(存储电量)
class Phone {
public:int power;Phone(int p) : power(p) {} // 源类构造
};// 目标类:充电宝(支持从手机转换,接收Phone对象的构造函数是关键)
class PowerBank {
public:int store;// 接收Phone的构造函数:支持Phone→PowerBank隐式转换PowerBank(const Phone& ph) : store(ph.power) {cout << "隐式转换:手机电量→充电宝存储" << endl;}void show() { cout << "充电宝存储:" << store << "%" << endl; }
};int main() {Phone myPhone(60); // 创建手机对象(60%电量)PowerBank pb = myPhone; // 隐式转换:手机→充电宝pb.show(); // 输出结果return 0;
}
c++中类型转换 ,以下代码由AI生成,仅供参考:
#include <iostream>
using namespace std;// 1. 演示:内置类型(int)转换为类类型(MyInt)
class MyInt {
private:int value;
public:// 单参数构造函数:实现 int -> MyInt(不加 explicit 允许隐式转换)MyInt(int v) : value(v) {cout << "内置类型 int 转换为 MyInt,值为:" << value << endl;}void showValue() {cout << "MyInt 的值:" << value << endl;}
};// 2. 演示:类类型(A)转换为另一种类类型(B)
class A {
private:int num;
public:A(int n) : num(n) {}// 类型转换函数:在源类 A 中定义,实现 A -> Boperator int() const {return num; // 先将 A 转为 int,再借助 B 的构造函数转 B}
};class B {
private:int data;
public:// 接收 A 对象的构造函数:在目标类 B 中定义,实现 A -> BB(const A& a) : data(static_cast<int>(a)) {cout << "类 A 转换为类 B,B 的值为:" << data << endl;}
};int main() {// 测试1:内置类型 int 转类类型 MyInt(隐式转换)MyInt obj1 = 10; // 等价于 MyInt obj1(10)obj1.showValue();// 测试2:类类型 A 转类类型 BA a(20);B b = a; // 调用 B 的构造函数,接收 A 对象完成转换return 0;
}
三、static成员
(1)用static修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。
(2)静态成员变量为所有类对象共享,不属于某个具体的对象,不存在对象中,存放在静态区。
(3)用static修饰的成员函数,叫做静态成员函数,静态成员函数没有this指针。
(4)非静态成员函数,可以访问任意的静态成员变量和静态成员函数。
(5)静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
(6)突破类域可以访问静态成员,可以通过 类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。
(7)静态成员也是类的成员,受public,protected,private访问限定符修饰。
(8)静态成员变量不能在声明的位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。
4、友元
- 分类:分为友元函数和友元类,声明时在函数或类前加 friend ,且声明放在类内部。
-—————— 友元函数:
-(1) 可访问类的私有和保护成员,但不是类的成员函数。
- (2)可在类内任意位置声明,不受访问限定符限制。
- (3)一个函数可作为多个类的友元函数。
- ———————友元类:
- (1)其所有成员函数都可访问另一个类的私有和保护成员。
-(2) 关系是单向的(如A是B的友元,B不一定是A的友元)且不可传递(A是B的友元、B是C的友元,A不是C的友元)。
- 优缺点:提供了突破封装的便利,但增加耦合度、破坏封装,不宜多用。
友元函数举例:
#include <iostream>
using namespace std;class Student {
private:string name;int score;
public:Student(string n, int s) : name(n), score(s) {}// 声明友元函数friend void showStudentInfo(Student& stu);
};// 友元函数:可访问Student的私有成员
void showStudentInfo(Student& stu) {cout << "姓名:" << stu.name << ",分数:" << stu.score << endl;
}int main() {Student s("小明", 95);showStudentInfo(s);return 0;
}
友元类:
#include <iostream>
using namespace std;class Car {
private:string brand;int price;
public:Car(string b, int p) : brand(b), price(p) {}// 声明友元类friend class CarDealer;
};class CarDealer {
public:// 可访问Car的私有成员void getCarInfo(Car& car) {cout << "汽车品牌:" << car.brand << ",价格:" << car.price << "万" << endl;}
};int main() {Car c("宝马", 35);CarDealer dealer;dealer.getCarInfo(c);return 0;
}
5、内部类
内部类是定义在另一个类内部的独立类,受外部类的类域和访问限定符限制,外部类对象不包含内部类。内部类默认是外部类的友元类,能访问外部类成员。它是一种封装方式,若类A与类B紧密关联且主要为类B服务,可将类A设为类B的内部类;若把类A放在 private 或 protected 位置,类A就成为类B的专属内部类,只能在类B内使用。
#include <iostream>
using namespace std;class Car { // 外部类
private:string brand = "Tesla";int price = 30;// 内部类:专门为Car服务,封装汽车的电子系统逻辑class ElectronicSystem { public:void checkBattery() {// 内部类可访问外部类私有成员cout << brand << " 电池状态正常,车辆价值 " << price << " 万" << endl;}};public:void startCar() {ElectronicSystem es; // 外部类中创建内部类对象es.checkBattery();cout << "汽车启动成功" << endl;}
};int main() {Car myCar;myCar.startCar();// 错误示例:无法在类外直接访问内部类 ElectronicSystem(因它是 Car 的私有内部类)// Car::ElectronicSystem es; return 0;
}
6、匿名对象
用类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象。
匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。
举例如下:
#include <iostream>
using namespace std;class Person {
private:string name;int age;
public:Person(string n, int a) : name(n), age(a) {cout << "构造函数调用" << endl;}~Person() {cout << "析构函数调用" << endl;}void showInfo() {cout << "姓名:" << name << ",年龄:" << age << endl;}
};// 函数:接收 Person 对象作为参数
void printPerson(Person p) {p.showInfo();
}int main() {// 示例1:匿名对象作为函数参数(临时传递,调用后立即析构)printPerson(Person("张三", 20)); // 示例2:匿名对象直接调用成员函数(调用后立即析构)Person("李四", 25).showInfo(); return 0;
}