网站策划书撰写,腾讯官方网站,网页平台做个业务推广,玉溪哪有网站建设服务公司目录 1.类的构成
2.this指针 3.C类里面的静态成员 3.1 类里面的静态成员函数
3.2 类里面的静态成员变量
静态成员变量的特点
共享性
存储位置
生命周期
访问权限
如何初始化#xff1f; 构造函数 1.类的构成 public修饰的成员在类外可以直接被访问
private和protecte…目录 1.类的构成
2.this指针 3.C类里面的静态成员 3.1 类里面的静态成员函数
3.2 类里面的静态成员变量
静态成员变量的特点
共享性
存储位置
生命周期
访问权限
如何初始化 构造函数 1.类的构成 public修饰的成员在类外可以直接被访问
private和protected修饰的成员不能在类外被访问
如果变量没有被这三个修饰词来修饰的话,则默认为private
2.this指针
关于this指针的一个经典回答 有一个房子类 当你进入这个房子的时候你可以看见桌子、椅子等但是房子的全貌你看不见了 对于一个类的实例来说你可以看见他的成员函数、成员变量 但是实例本身呢 this这个指针就时时刻刻指向这个实例本身 this指针是指向对象的指针存放对象的首地址 #include iostream
using namespace std;
class Student
{
public:void Setage(int age){this-age age;//this-age表示这个类所指向的age,单独的age是形参}void Showage(){cout age endl;}private:int age;
};
int main()
{Student s;s.Setage(18);s.Showage();return 0;
}
//输出结果为18 #include iostream
using namespace std;
class Student
{
public:void Setage(int age){age age;//ageage,并不会改变类里面age的值}void Showage(){cout age endl;}private:int age;
};
int main()
{Student s;s.Setage(18);s.Showage();return 0;
}
//输出结果是0 this指针是编译器帮我们加上的不占用空间 #include iostream
using namespace std;
class Student
{
private:int age;
};int main()
{cout sizeof(Student) endl;//结果为4return 0;
} 既然this指针指向类那么类的地址和this的地址是一样的 #include iostream
using namespace std;
class Student
{
public:void test(){cout this endl;}private:int age;
};int main()
{Student s;s.test();cout s endl;return 0;
} 输出的地址是一样的 3.C类里面的静态成员
静态成员是属于整个类的而不是某个对象静态成员只存储一份供所有对象使用 3.1 类里面的静态成员函数
静态成员函数没有隐藏的this指针 #include iostream
using namespace std;
class Student
{
public:void Set(int age){this-age age;}void Showage(){cout age endl;}static void test(){coutthis-ageendl;//这个地方的age会报错,如果不加this也会报错//静态成员函数不能引用非静态成员,但是可以在类的非静态成员函数里面使用静态成员}
private:int age;
};int main()
{Student s;return 0;
} 这就说明了静态函数是在这个类创建出来之前就出现了也就是说静态函数的创建时间比这个类早所以在这个静态函数里面使用类里面的东西会报错 那么应该如何使用这个函数呢 首先要注意一点这个静态函数不能使用类里面的东西 class Student
{static void test(){couti want to sleependl;}
};int main()
{Student s;Student::test();//这里记住不能通过类名调用非静态成员函数s.test();//上面两句说明类的对象可以使用静态和非静态成员函数return 0;
} 这两种方法都可以调用这个静态函数 3.2 类里面的静态成员变量
静态成员变量在类的所有实例之间共享并且可以在不创建类的情况下访问在类创建之前就创建好了
3.3静态成员变量的特点
共享性
静态成员变量在类所有的实例之间共享这个类的每个对象的静态变量都是相同的
存储位置
静态成员变量存储在静态存储区而不是每个对象的堆栈中。这使得他们可以在不创建类的实例的情况下访问
生命周期
与程序的生命周期一样。在程序启动的时候就创建了在程序结束的时候就销毁
访问权限
静态成员变量可以通过类名来访问也可以通过对象来访问。但是建议前者这样子能强调他们的共享性
如何初始化 class Student
{
private:static int a;
};int Student::a0; 4构造函数
构造函数的主要作用不是开辟空间创建对象而是为了初始化对象
特征
他不是开空间创建对象而是初始化对象
1. 无返回值也不用写void
2. 函数名与类名相同
3. 构造函数可以重载 #include iostreamusing namespace std;class Student
{
public:Student(int a,int b,int c)//构造函数{_aa;_bb;_cc;}Student(int a,int b){_aa;_bb;}void Show(){printf(%d %d %d,_a,_b,_c);}
private:int _a,_b,_c;
};int main()
{Student s(1,2,3);//创建s对象的同时调用构造函数Student p(1,2);//函数重载s.Show();return 0;
}
//输出结果1 2 3 #include iostreamusing namespace std;class Student
{
public:void Show(){printf(%d %d %d,_a,_b,_c);}
private:int _a,_b,_c;
};int main()
{Student s;s.Show();return 0;
}
/*
输出结果32767 0 0
如果类里面没有构造函数,则C编译器会自动调用一个无参的默认构造函数
*/ private:int _a1,_b1,_c1;/*
这里对_a,_b,_c不是赋值不是初始化而是给缺省值
原因这里的变量都是声明还没有创建出来
*/ 5析构函数
作用与构造函数相反。在对象销毁时自动调用完成对象中资源的清理工作
特征
析构函数名是在类名的签名加~
无参数无返回值
一个类只能有一个析构函数如果自己没有定义系统会调用默认的析构函数。析构函数不能重载
对象生命周期结束时调用
析构函数的执行在return之前 #include iostream
using namespace std;
class S
{
public:S(){cout 构造 endl;}~S(){cout 析构 endl;}
};
int main()
{S s;return 0;
} 先输出构造然后再输出析构 作用
当我们在类中声明了一些指针变量的时候我们一般在析构函数里面释放这些指针变量所占有的空间因为系统不会释放指针变量指向的空间我们需要自己来delete
6.构造函数和析构函数的一些应用
6.1 定义为局部变量
先补充一个知识点局部变量存储在栈区全局变量和静态变量存储在静态区 #include iostreamusing namespace std;class Date
{
public:Date(int a){_a a;cout Date()- _a endl;}~Date(){cout ~Date()- _a endl;}private:int _a;
};int main()
{Date a(1);Date b(2);return 0;
} 输出结果 析构函数的时候为什么先输出~Date()-2呢 首先我们要知道a和b变量都是局部变量都存储在栈区栈区遵循后进先出的原则 6.2 局部变量和全局变量同时存在时 #include iostreamusing namespace std;class Date
{
public:Date(int a){_a a;cout Date()- _a endl;}~Date(){cout ~Date()- _a endl;}private:int _a;
};Date b(2);int main()
{Date a(1);return 0;
} 输出结果 全局变量先定义在程序结束之后再销毁在局部变量销毁之后再销毁 6.3 静态变量、局部变量、全局变量共存 #include iostreamusing namespace std;class Date
{
public:Date(int a){_a a;cout Date()- _a endl;}~Date(){cout ~Date()- _a endl;}private:int _a;
};Date b(2);int main()
{Date a(1);static Date c(3);return 0;
} 输出结果 创建变量按顺序来。首先是局部变量被销毁静态区的变量只考虑这个区域时在销毁时也是类似“后进先出”一样来销毁 6.4 静态变量在循环中 #include iostreamusing namespace std;class Date
{
public:Date(int a){_a a;cout Date()- _a endl;}~Date(){cout ~Date()- _a endl;}private:int _a;
};int main()
{for (int i 0; i 1; i){static Date a(1);Date b(2);}return 0;
} 输出结果 静态变量只会被创建一次所以只有一次构造函数和一次析构函数 7.拷贝构造函数
浅拷贝
拷贝成员变量的值 #include iostreamusing namespace std;class Student
{
public:Student(int a, int b, int c){_a a;_b b;_c c;}/*当有Student b(a)这行代码的时候问题1为什么要写成 Student s呢写成Student s表示s是a的别名,不需要调用拷贝构造函数如果写成Student s的话,会无限递归原因在调用拷贝构造函数的时候,
Student(Student s){this-_a s._a;this-_b s._b;this-_c s._c;}
把a传给s也会调用拷贝够咱函数,也就是说,调用一个拷贝构造函数会使得其内部嵌套另一个拷贝构造函数
那这个嵌套的拷贝构造函数也会继续嵌套一个,依次无限嵌套下去而导致死循环;所以要加一个问题2那为什么加一个const呢因为我们要a的值不变,将其内部的值赋给b(s),所以我们最好加一个const来修饰来让a的值不变
只要a里面的值变,就会报错
因为有可能出现以下情况
Student(const Student s){a._a this-_a;a._b this-_b;a._c this-_c;}
如果没有const,出现上述情况编译器是不会报错的*/Student(const Student s){this-_a s._a;this-_b s._b;this-_c s._c;}void Show(){printf(%d %d %d\n, _a, _b, _c);}private:int _a, _b, _c;
};int main()
{Student a(10, 20, 30);Student b(a);b.Show();return 0;
} 输出结果 8.重载
关键字operator
8.1 运算符重载 #include iostream
using namespace std;
class Student
{
public:Student(int a, int b, int c){_a a;_b b;_c c;}bool operator(const Student b)//重载运算符{return _a b._a _b b._b _c b._c;//a和b相等的条件}
private:int _a, _b, _c;
};int main()
{Student a(10, 20, 30);Student b(10, 20, 30);cout (a b) endl;//这里判断a和b是否相等只需要判断a和b里面的_a,_b,_c是否分别相等return 0;
} #include iostream
using namespace std;
class Student
{
public:Student(int a, int b, int c){_a a;_b b;_c c;}int operator-(const Student b){return b._c - _c;}private:int _a, _b, _c;
};int main()
{Student a(10, 20, 30);Student b(10, 20, 50);cout (b - a) endl;return 0;
}
//输出结果是a._c - b._c 结果为-20 这里就有一个问题 void operator是怎么区别前置和后置的呢 int operator();//前置int operator(int);//后置
//规定:()里面无参数的为前置,否则为后置 注意点 #include iostream
using namespace std;
class A
{
public:A(int val){_valval;}void operator(const A d){_vald._val;}void operator(const A d){_vald._val;}void Print(){cout_valendl;}
private:int _val;
};
int main()
{A a(3),b(4),c(7);cab;
/*
cab会报错,但是ab,不会报错
因为cab这句话首先执行的是ab,然后返回值是void
所以
void operator(const A d){_vald._val;}
应该改成
A operator(const A d){_vald._val;return *this;//这里返回值是*this的一个拷贝(拷贝的话可能调用其中的拷贝函数或者构造函数什么的)}
所以最好写成
A operator(const A d){_vald._val;return *this;//这里返回的是*this的别名,不会再去调用拷贝函数}
*/a.Print();return 0;
} 8.2 流重载 #include iostreamusing namespace std;class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}void operator(ostream out) // out是cout的别名{out _year / _month / _day endl;}void operator(istream in){in_year_month_day;}private:int _year, _month, _day;
};int main()
{Date a(2023, 9, 16);acin;//a是被操作的对象,cin传给inacout;return 0;
} const的参与 #include iostream
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){printf(%d %d %d\n, _year, _month, _day);}private:int _year, _month, _day;
};
int main()
{Date a(2023, 9, 17);const Date b(1,1,1);a.Print();b.Print();//这里会报错
/*
因为b是一个const Date类型的
b.Print()的原型是b.Print(b),b是一个const Date*类型的
void Print()的原型是void Print(Date* const this)
b从const Date*变成Date*权限放大了
所以我们将void Print()写成void Print() const,这样他的原型变成了:
void Print(const Date* const this)是const Date*类型,后面那个const是修饰this
让this一直指向这个类且不能变
*/return 0;
} #include iostream
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print()const{printf(%d %d %d\n, _year, _month, _day);}bool operator(const Date d){return _yeard._year;}private:int _year, _month, _day;
};
int main()
{Date a(2023, 9, 17);const Date b(1,1,1);ab;//不会报错ba;//这里会报错
/*
bool operator(const Date d)的原型是
bool operatorDate* const this,const Date d)
所以写成ba的时候(b是const Date*类型)
b由const Date*变成Date*,权限变大
可以写成
bool operator(const Date d)const
*/return 0;
} 9.初始化列表
初始化列表可以认为是成员变量定义的地方
private里面是声明
9.1格式
#include iostream
using namespace std;
class Date
{
public:
//格式如下Date(int a): _a(a),_b(a){}void Print(){cout _a endl;}private:int _a;int _b;
};
int main()
{Date a(6);a.Print();return 0;
}
9.2例子
每个成员变量在初始化列表中只能出现一次
类中包含以下成员必须放在初始化列表位置进行初始化
1.引用成员变量
2.const成员变量
3.自定义类型成员 class A
{
public:A(int a): _a(a){}void Print(){cout _a endl;}private:int _a;
};class B
{
public:B(int a, int b): _b(b),_a(a),_n(10),_r(b){}void Print(){_a.Print();cout _b endl;}private:int _b;A _a;const int _n;int _r;
}; 成员变量在类中声明的次序就是其在初始化列表里面初始化的次序与其在初始话列表里面的先后顺序无关
例子
#include iostreamusing namespace std;class A
{
public:A(int a):_a(a),_b(_a){}void Print(){cout_aendl;cout_bendl;}
private:int _b;int _a;
};int main()
{A a(3);a.Print();return 0;
}
/*
输出结果是 3 随机值
因为先是把_a赋值给_b,再把a赋值给_a
*/
9.3explicit关键字
用explicit修饰的构造函数会禁止单参构造函数的隐式转换
首先搞明白什么是隐式转换 class A
{
public:A(int a)//单参构造函数: _a(a){}private:int _a;
};//定义一个类对象
A a(10);
/*
此时_a的值为10,如果进行以下操作,a20,这样子会把_a的值变为20
编译器会用20(将20赋值给无名对象的_a)构造一个无名对象
最后用无名对象赋值给对象a,这种转换就叫隐式转换
*/ 隐式转换其实还有很多比如 int a2; double ba; 编译器会先引入一个中间变量类型为double然后再将这个值赋值给b #include iostreamusing namespace std;class A
{
public:A(int a)
/*
如果这里改成explicit A(int a)的话
a5那行代码会报错
因为explicit会禁止单参构造函数的隐式转换
*/: _a(a){}void Print(){cout _a endl;}private:int _a;
};int main()
{A a(3);a 5;a.Print();return 0;
} 9.4 匿名对象
生命周期只有它所在的那一行 class A
{
public:A(int a):_aa{}
private:int _a;
}; A a(10);a A(20);a.Print();如果不使用匿名对象那就需要
A a(10);
A b(20);
ab;
这样子来操作
/*
A(20)表示创建一个匿名对象,他的_a值为20
这个匿名对象创建的时候也会调用构造函数和析构函数
*/ 10.友元类
如果类A是类B的友元类那么B可以调用A里面的private成员和protected成员
但是这样子做会破坏类的封装性
注意点
1. 友元关系不能被继承
2. 友元关系是单向的A是B的友元但是B不一定是A的友元
3. 友元不具有传递性比如A是B的友元B是C的友元但是A不是C的友元 #include iostreamusing namespace std;class A
{
public:friend class B;//代表A是B的友元类,B可以访问A里面的所有成员A(int a){_aa;}
private:int _a;
};class B
{
public:B(int b){_bb;}void Show_A_a(A a){couta._aendl;//这里就体现了可以访问A定义的对象a里面的私有成员}
private:int _b;
};int main()
{A a(666);B b(555);b.Show_A_a(a);return 0;
} 10. 2内部类
定义如果一个类定义在另一个类的里面那这个里面的类就叫做内部类
内部类是外部内的友元类但是外部类不是内部类的友元类
比如A是外部类B是外部类
那么定义一个类的时候应该A::B b;定义一个对象bb能调用A和B里面的成员但是如果A a那a不能调用B里面的成员