C++核心编程三(初始化列表、静态成员、this指针、成员函数与变量、友元、运算符重载)
2023-12-29 20:08:18
文章目录
基于b站黑马c++视频做的笔记,仅供参考和复习!!!
初始化列表
//初始化列表
class Person
{
public:
//传统初始化操作
/*Person(int a, int b, int c){
m_A = a;
m_B = b;
m_C = c;
}*/
//初始化列表初始化属性
Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c){
}
int m_A;
int m_B;
int m_C;
};
void test01()
{
//Person p(10, 20, 30);
Person p(30,20,10);
cout << "m_A = " << p.m_A << endl;
cout << "m_B = " << p.m_B << endl;
cout << "m_C = " << p.m_C << endl;
}
int main()
{
test01();
}
类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为:对象成员,且在析构函数中,利用初始化列表的方法来赋值。
//类对象作为类成员
class Phone
{
public:
Phone(string pName){
cout << "Phone的构造函数调用" << endl;
m_PName = pName;
}
string m_PName;
};
class Person
{
public:
//Phone m_Phone = pName 即---> Phone m_Phone = "苹果MAX" 隐式转换法赋初值操作Person p4 = 10;
Person(string name, string pName) :m_Name(name), m_Phone(pName){
cout << "Person的构造函数调用" << endl;
}
//姓名
string m_Name;
//手机
Phone m_Phone; //Phone类对象 作为 Person类成员
};
//当其他类对象作为本类成员,构造时先构造类对象,再构造自身,而析构顺序与构造相反
void test01()
{
Person p("张三", "苹果MAX");
cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}
int main()
{
test01();
}
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:
- 静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
class Person
{
public:
static int m_A;
//静态成员变量也是有访问权限的
private:
static int m_B;
};
//类外初始化
int Person::m_A = 100;
int Person::m_B = 10;
void test01()
{
Person p;
cout << p.m_A << endl;
Person q;
q.m_A = 200; //所有对象共享一份数据,
//p1对象修改了值,其他对象如p去访问的就是修改的值
cout << "q的m_A是:" << q.m_A << endl;
cout <<"p的m_A是:"<< p.m_A << endl; //p.m_A访问静态成员变量的值 输出200
}
void test02()
{
//静态成员变量 不属于某个对象上,所以对象都共享同一份数据
//因此静态成员变量有两种访问方式
//1、通过对象进行访问
Person p;
cout << p.m_A << endl;
//2、通过类名进行访问
cout << Person::m_A << endl;
//cout << Person::m_B << endl; 类外无法访问私有的静态成员变量
}
int main()
{
test01();
//test02();
}
test01()输出结果:
- 静态成员函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
class Person
{
public:
//静态成员函数
static void func(){
m_A = 100; //静态成员函数可以访问静态成员变量 所有对象共有的
//m_B = 200; //静态成员函数 不可以访问 非静态成员变量
//无法区分到底是那个对象的m_B的属性
cout << "static void func的调用" << endl;
}
static int m_A; //静态成员变量 共享的
int m_B; //非静态成员变量
//静态成员函数也是有访问权限的
private:
static void func2(){
cout << "static void func2的调用" << endl;
}
};
int Person::m_A = 0;
//两种访问方式
void test01()
{
//1、通过对象访问
Person p;
cout << p.m_A << endl; //类外赋值 int Person::m_A = 0;
p.func();
cout << p.m_A << endl; //静态函数内赋值
//2、通过类名访问
Person::func();
//Person::func2(); 类外访问不到私有静态成员函数
}
int main()
{
test01();
}
成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
//成员变量 和 成员函数 分开存储的
class Person
{
int m_A; //非静态成员变量 属于类的对象上
static int m_B; //静态成员变量 不属于类对象上 类中不占用内存
void fun() {} //非静态成员函数 不属于类对象上 大家类共享
static void func() {} //静态成员函数 不属于类对象上 大家类共享
};
int Person::m_B = 0;
void test01()
{
Person p;
//空对象占用内存空间为:1
//C++编译器会给每个空对象分配一个字节空间,是为了区别空对象占内存的位置
//每个空对象也应该有一个独一无二的内存地址
cout << "size of p = " << sizeof(p) << endl;
}
void test02()
{
Person p;
cout << "size of p = " << sizeof(p) << endl; //4个字节
}
int main()
{
//test01();
test02(); //注释掉 int m_A; 输出为空 1字节
}
this指针
this指针指向被调用的成员函数所属的对象。
class Person
{
public:
Person(int age){ //有参构造函数
//this指向的是被调用的成员函数 所属的对象
this->age = age;
}
Person& PersonAddAge(Person &p){
this->age += p.age;
//this指向p2的指针,而*this解引用指向的就是p2这个对象
return *this;
}
//若Person&改为Person则为值传递 此时*this指向的对象会进行
//形如 Person p = p'的拷贝构造函数调用返回的则不是p2本体,而是其他对象,
//但是输出的是p2.age,所以最终值为p2.age = 20
int age;
};
//1、解决名称冲突
void test01()
{
Person p1(18);
cout << "p1的年龄:" << p1.age << endl;
}
//2、返回对象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
//链式编程思想
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main()
{
//test01();
test02();
}
空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
//空指针调用成员函数
class Person
{
public:
void showClassName(){
cout << "this is Person class" << endl;
}
void showPersonAge(){
//报错原因是因为传入的指针是为NULL 解决办法如下
if (this == NULL){
return;
}
cout << "age = " << m_Age << endl; //属性前面默认其实为this->m_Age,但当前this又指向空的值,所以发生错误
}
int m_Age;
};
void test01()
{
Person * p = NULL;
p->showClassName(); //空指针可以调用成员函数
p->showPersonAge();
}
int main()
{
test01();
}
const修饰成员函数
this指针的本质 是指针常量 指针的指向是不可以修改的
const Person * const this
在成员函数后面加const,修饰的是this指向以及指针指向的值都也不可以修改。
//常函数
class Person
{
public:
Person(int x):m_A(x){};
void showPerson() const
{
m_B = 100;
//this->m_A = 100;
//this = NULL; //this指针不可以修改指针的指向的
}
void func(){
m_A = 100;
}
int m_A;
mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
};
// void test01()
// {
// Person p;
// p.showPerson();
// }
//常对象
void test02()
{
const Person p(50); //在对象前加const,变为常对象 vscode中 常对象必须初始化 因此对代码进行了改进
//p.m_A = 100;
p.m_B = 200; //特殊变量 在常对象下可以修改
//常对象只能调用常函数
p.showPerson();
//p.func(); //常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性,而常对象不允许修改属性
}
int main()
{
//test01();
test02();
}
友元
友元的关键字为 friend
友元的目的就是让一个函数或者类 访问另一个类中私有成员。
友元的三种实现
- 全局函数做友元
- 类做友元
- 成员函数做友元
1、全局函数做友元
#include <iostream>
using namespace std;
//建筑类
class Building
{
//goodGay全局函数是Building好朋友,可以访问Building中私有成员 即卧室
friend void goodGay(Building* building);
public:
Building(){ //构造函数
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
//全局函数
void goodGay(Building *building)
{
cout << "好基友全局函数 正在访问:" << building->m_SittingRoom << endl;
cout << "好基友全局函数 正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
Building building;
goodGay(&building); //利用指针调用 还可以直接1、对象调用(值传递) 2、引用调用
}
int main()
{
test01();
}
2、类做友元
//类做友元
class Building
{
//GoodGay是Building好朋友,可以访问Building中私有成员
friend class GoodGay;
public:
Building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
class GoodGay
{
public:
GoodGay(); //构造函数
void visit(); //参观函数,访问Building中的属性
Building * building;
};
//类外写成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
//创建建筑物对象
building = new Building; //创建开辟一个Building类型的指针,返回该地址,building指向开辟一个内存地址
//且会调用Building中的构造函数
// 加括号调用有参构造函数,不加括号调用默认构造函数或唯一的构造函数,看需求
}
void GoodGay::visit()
{
cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
GoodGay gg; //创建GoodGay对象后会马上调用GoodGay::GoodGay()构造函数
gg.visit();
}
int main()
{
test01();
}
3、成员函数做友元
class Building; //类 声明
class GoodGay
{
public:
GoodGay();
void visit(); //让visit函数可以访问Building中的私有成员
void visit2();//让visit函数不可以访问Building中的私有成员
Building * building;
};
class Building
{
//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
friend void GoodGay::visit();
public:
Building(); //构造函数
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
//类外实现成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
//创建建筑物对象
building = new Building;
}
void GoodGay::visit()
{
cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
cout << "visit2函数 正在访问:" << building->m_SittingRoom << endl;
//cout << "visit2函数 正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
GoodGay gg;
gg.visit();
gg.visit2();
}
int main()
{
test01();
}
加号运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
作用:实现两个自定义数据类型相加的运算。
//加号运算符重载 实现两个自定义数据类型相加的运算
class Person
{
public:
//1、 成员函数重载+号
/*Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}*/
int m_A;
int m_B;
};
//2、全局函数重载+号
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载的版本
Person operator+(Person &p1,int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test01()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
//1、成员函数重载本质调用
//Person p3 = p1.operator+(p2);
//2、全局函数重载本质调用
//Person p3 = operator+(p1, p2);
//由于使用编译器提供的函数名operator+ 可简化为下面这种形式调用
Person p3 = p1 + p2;
cout << "p3.m_A = " << p3.m_A << "\t p3.m_B = " << p3.m_B << endl;
Person p4 = p1 + 100; //Person + int
cout << "p4.m_A = " << p4.m_A << "\t p4.m_B = " << p4.m_B << endl;
}
int main()
{
test01();
}
左移运算符重载
作用:可以输出自定义数据类型
//左移运算符重载 可以输出自定义数据类型
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a, int b){
m_A = a;
m_B = b;
}
private:
//利用成员函数重载 左移运算符 p.operator<<(cout) 简化 p << cout ;
//不能利用成员函数重载<<运算符,因为无法实现cout在左侧
/*int operator<<()
{
}*/
int m_A;
int m_B;
};
//只能利用全局函数重载左移运算符
ostream& operator<<(ostream & cout,Person & p)
{
cout << "m_A = " << p.m_A << " m_B = " << p.m_B ;
return cout;
}
void test01()
{
Person p(10,10);
//p.m_A = 10;
//p.m_B = 11;
cout << p << " hello world" << endl;
}
int main()
{
test01();
}
递增运算符重载
#include <iostream>
using namespace std;
//重载递增运算符 实现自己的整型数据
//自定义整型
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger(){
m_Num = 0;
}
//重载前置++运算符 返回引用为了一直对一个数据进行递增操作
MyInteger& operator++(){
m_Num++;
return *this;
}
//重载后置++运算符
//void operator++(int) int代表占位参数,可以区分前置和后置递增
MyInteger operator++(int){
//先 记录当时结果
MyInteger temp = *this; //保存了值传递里的值
//后 递增
m_Num++;
//最后记录结果做返回
return temp;
}
private:
int m_Num;
};
//重载<<运算符
ostream & operator<<(ostream & cout, MyInteger myint)
{
cout << myint.m_Num;
return cout;
}
void test01()
{
MyInteger myint;
cout << ++(++myint) << endl;
cout << myint << endl;
}
void test02()
{
MyInteger myint;
cout << (myint++) << endl;
cout << myint << endl;
}
int main()
{
//test01();
test02();
}
赋值运算符重载
#include <iostream>
using namespace std;
//赋值运算符重载
class Person
{
public:
Person(int age){
m_Age = new int(age); //在堆区开辟内存 类似于new int(18)
}
~Person(){
if (m_Age != NULL){
delete m_Age;
m_Age = NULL;
}
}
//重载 赋值运算符
Person& operator=(Person& p){
//编译器提供的是浅拷贝
//m_Age= p.m_Age;
//应该先判断是否有属性在堆区,如果有显示释放干净,然后再深拷贝
if (m_Age != NULL){
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
//返回对象自身
return *this;
}
int* m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1; //类对象的赋值操作
cout << "p1的年龄为:" << *p1.m_Age << endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main()
{
test01();
}
关系运算符重载
//关系运算符重载
class Person
{
public:
Person(string name, int age){
m_Name = name;
m_Age = age;
}
//重载 == 号
bool operator==(Person& p){
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){
return true;
}
return false;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("张三", 18);
Person p2("张三", 18);
if (p1 == p2){
cout << "p1和p2是相等的" << endl;
}
else{
cout << "p1和p2是不相等的" << endl;
}
}
int main()
{
test01();
}
函数调用运算符重载
//函数调用符重载
//打印输出类
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test){
cout << test << endl;
}
};
void MyPrint02(string test)
{
cout << test << endl;
}
void test01()
{
MyPrint myPrint;
myPrint("hello world"); //类作为函数调用 由于使用起来非常类似于函数调用,因此称为仿函数
MyPrint02("hello world2"); //常规调用
}
//仿函数非常灵活,没有固定的写法
//加法类
class MyAdd
{
public:
//重载函数调用运算符
int operator()(int a, int b){
return a + b;
}
};
void test02()
{
MyAdd add;
cout << add(1, 3) << endl;
//匿名函数对象 MyAdd()这种形式称为匿名对象,使用完后立即释放
cout << MyAdd()(1, 6) << endl;
}
int main()
{
//test01();
test02();
}
文章来源:https://blog.csdn.net/qq_45009309/article/details/135249890
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!