#include
using namespace std;
#include
//类的定义有:类的访问权限,类的属性和类的行为
class student {
//访问权限:公共权限
public:
//类的属性
string name;
string ID;// 学号
//类的行为 用函数实现 打印输出学生的姓名和学号
void print_student() {
cout << "学生的姓名:" << name<<"\t" << "学生的学号:" << ID << endl;
}
//对类初始化可以在行为中用函数实现
void InitName(string cname) {
name = cname;
}
void InitID(string cID)
{
ID = cID;
}
};
//拓展(专业术语)
//类中的属性和行为,我们统一称为成员
//属性 成员属性 成员变量(别名)
//行为 成员函数 成员方法(别称)
int main()
{
//创建一个具体的对象
//实例化(通过一个类,创建一个对象的过程)
student s;
//为对象的属性赋值
s.name = "张三";
s.ID = "18249723507";
//也可以用行为对对象进行初始化
s.InitName("张三");
s.InitID("18249723507");
s.print_student();//相当于调用类的行为中的函数
system("pause");
return 0;
}
//类的访问权限 三种
//公共权限 public 成员 类内可以访问 类外不可以访问
//保护权限 protected 成员 类内可以访问 类外不可以访问 儿子可以访问父亲中的保护内容
//私有权限 private 成员 类内可以访问 类外不可以访问 儿子不可以访问父亲的私有内容
class person
{
//公共权限
public:
string name;
//保护权限
protected:
string car;
//私有权限
private:
string idcard;
//行为设为公共权限
public:
void Init() {
//类内都可以访问
name = "张三";
car = "奔驰";
idcard = "123456";
}
};
//struct默认权限是 共有,class默认权限是私有
class c {
int a;
};
struct c1 {
int a;
};
注:一般来说,类中成员的属性一般都是设置为私有权限,而为了对这些属性进行操作,因此我们需要在类的行为中提供一些公共的接口用于操作这些私有的属性;
#include
using namespace std;
#include
//成员设置为私有
//1、可以自己控制读写权限 2、对于写可以检测数据的有效性
//定义一个类 称为人
class person {
//公有的行为/函数
public:
//对name可读可写
void set_name(string cname) {//设置姓名
name = cname;
}
string get_name() {//获取姓名
return name;
}
//对age可读
int get_age() {//获取年龄
return age;
}
//对idol仅可写
void set_idol(string cidol)//设置喜欢的偶像
{
idol = cidol;
}
/*string get_idol() {
return idol;
}*/
//私有的成员属性
//2、写可以检测数据的有效性(本质是成员函数中用一个if语句判断输入值的合法性)
void set_age(int c_age)
{
while (1) {
if (c_age < 0 || c_age>150) {
cout << "您输入的age值非法,请重新输入" << endl;
cin >> c_age;
age = c_age;
}
else
break;
}
}
private:
string name;
int age=18;//初值为18岁
string idol;
};
int main()
{
//1、可以自己控制读写权限
//对象具体化
person p;
p.set_name("张三");//由于name可写,因此可对其进行赋值操作(实际上为类中有写name的公共权限的函数)
cout << "姓名:" << p.get_name() << endl;//name可读
cout << "年龄:" << p.get_age() << endl;//年龄可读
//p.get_age(20);//报错,因为age仅可读(即p中无可对age修改的公共权限的函数)
p.set_idol("星野爱");
//cout << "张三喜欢的偶像为:" << p.get_idol() << endl;//没有读的权限(p中无获取idol信息的函数)
// 2、对于写可以检测数据的有效性
p.set_age(160);//输入非法,重新输入
system("pause");
return 0;
}
#include
using namespace std;
#include
//利用全局函数和成员函数判断两个立方体是否相等
class cube {
//成员行为设为公共权限
public:
//设置长
void set_length(int l) {
m_l = l;
}
//获取长
int get_length()
{
return m_l;
}
//设置宽
void set_width(int w) {
m_w = w;
}
//获取宽
int get_width()
{
return m_w;
}
//设置高
void set_high(int h) {
m_h = h;
}
//获取高
int get_high()
{
return m_h;
}
//判断两立方体是否相等
//由于在类中可以访问类本身的属性,因此在比较两个cube是否相等,只需要传入另一个cube的信息即可比较
//此即为全局函数与成员函数的一个较大的区别
bool isSameByClass(cube c)
{
if (m_l == c.m_l && m_w == c.m_w && m_h == c.m_h)
{
return true;
}
else
return false;
}
//类的属性设为私有
private:
int m_l;//长
int m_w;//宽
int m_h;//高
};
bool isSame(cube c1, cube c2) {
if (c1.get_length() == c2.get_length() && c1.get_width() == c2.get_width() && c1.get_high() == c2.get_high())
{
return true;
}
else
return false;
}
int main()
{
//创建两个具体的cube对象
cube c1;
cube c2;
//对c1,c2进行初始化
c1.set_length(10);
c1.set_width(10);
c1.set_high(11);
c2.set_length(10);
c2.set_width(10);
c2.set_high(11);
bool ret = c1.isSameByClass(c2);
if (ret)
{
cout << "isSameByClass判断的两个立方体相等" << endl;
}
else
cout << "isSameByClass判断的两个立方体不相等" << endl;
ret = isSame(c1, c2);
if (ret)
{
cout << "isSame判断的两个立方体相等" << endl;
}
else
cout << "isSame判断的两个立方体不相等" << endl;
system("pause");
return 0;
}
pow的用法及定义介绍
#include
//pow是一个用来实现幂函数的调用接口,pow(base,ex)---即计算base的ex次方
即一个类中可以包含另一个类
例如:
当写一个较大的项目时,若将类都写到一个文件中,则会造成文件过大或降低代码可读性,同时也会降低多人完成一个大项目的写代码效率。因此,分文件定义类是非常重要的。
1、头文件中只需要包含对这个类的属性的定义以及行为的声明
2、对于每个类都会有相应的.cpp文件用于这个成员函数的具体实现
3、在对类中函数进行实现时,需加上 circle(类对应的名字):: ,用来标记此函数并非是全局函数,而是成员函数
point.h(点的头文件)(包含对点这个类的属性的定义以及行为的声明)
#pragma once//使此代码重复
//点的头文件
#include
using namespace std;
//头文件中只需要包含类的声明即可
//对点的类的声明
class point {
//类的行为
public:
//对x,y操作的声明
void set_x(int x);
int get_x();
void set_y(int y);
int get_y();
//类的属性
private:
int m_x;//x的坐标
int m_y;//y的坐标
};
circle.h(圆的头文件)包含对圆这个类的属性的定义以及行为的声明)
#pragma once
//圆的头文件
#include"point.h"
//头文件中只需要包含类的声明即可
对圆的类的声明
class circle {
//对成员函数的声明
public:
//对成员属性中存在的参数进行操作
void set_center(point center);//设置圆心
point get_center();//获取圆心
void set_r(int r);//设置半径
int get_r();//获取半径
//成员属性
private:
//注意:类中可以包含其他的类(类中类)
//圆心
point m_center;
//半径
int m_r;
};
point.cpp(点这个成员函数的具体实现)
#include"point.h"
//成员行为/函数的实现
//在对类中函数进行实现时,需加上 point(类对应的名字):: ,用来标记此函数并非是全局函数,而是成员函数
void point::set_x(int x) {//设置x
m_x = x;
}
int point::get_x() {//获取x
return m_x;
}
void point::set_y(int y) {//设置y
m_y = y;
}
int point::get_y() {//获取y
return m_y;
}
circle.cpp(圆这个成员函数的具体实现)
#include"circle.h"
在对类中函数进行实现时,需加上 circle(类对应的名字):: ,用来标记此函数并非是全局函数,而是成员函数
void circle::set_center(point center){//设置圆心
m_center = center;
}
point circle::get_center() {//获取圆心
return m_center;
}
void circle::set_r(int r) {//设置半径
m_r = r;
}
int circle::get_r() {//获取半径
return m_r;
}
relation.cpp(用于实现对点与圆关系的计算及结果输出)
此文件中主要实现了对类的初始化,及通过调用已经实现的类的接口来实现要实现的功能
//示例:计算点和圆之间的关系
#include"circle.h"
#include//使用pow需要调用的头文件
//pow是一个用来实现幂函数的调用接口,pow(base,ex)---即计算base的ex次方
//计算并打印给定点与圆之间的关系
void relation_point_circle(circle c,point p)
{
//计算圆心与点之间的距离 distance=距离公式的平方
int distance = pow((p.get_x() - c.get_center().get_x()), 2) +
pow((p.get_y() - c.get_center().get_y()), 2);
if (distance == pow(c.get_r(),2))//距离的平方等于圆心的平方
{
cout << "点在圆心上" << endl;
}
else if (distance > pow(c.get_r(), 2))
cout << "点在圆心外" << endl;
else
cout << "点在圆心内" << endl;
}
int main()
{
//类的实例化
point center;//圆心(0,0)
center.set_x(0);
center.set_y(0);
point p;//点
p.set_x(0);
p.set_y(11);
circle c;//圆
c.set_center(center);//设置圆心为(0,0)
c.set_r(10);//半径为10
relation_point_circle(c, p);
system("pause");
return 0;
}
主要作用于创建对象时为对象成员属性进行赋值(即初始化操作),构造函数由编译器自动调用,无需手动调用
主要作用于对象销毁前系统自动调用,执行一些清理工作(即清除类中的数据,以保护类中数据的安全。类似于不用的手机进行格式化,以保护用户数据安全)
注:对象的初始化和清理工作是编译器强制我们要做的事情,因此如果我们不提供构造函数和析构函数,编译器会提供。 但编译器提供的构造和析构函数都是空实现(即函数内部无任何代码)
构造函数语法:类名(){}
1、构造函数,没有返回值也不写void
2、函数名称与类名相同
3、构造函数可以有参数(即可以传参,本质即我们可以调用构造函数对对象成员属性进行初始化操作)
4、程序在调用对象时会自动调用构造函数,无需手动调用,而且只需要调用一次
析构函数语法与构造函数类似,区别有:
2、函数名称与类名相同,但函数名称前要加~
3、析构函数不可以有参数(对数据格式化,不需要传参),因此不可以发生重载
//例如
定义一个类
class person{
public:
person(int a) {
cout << "此为构造函数" << endl;
age = a;
}
~person() {
cout << "此为析构函数" << endl;
}
int get_age()
{
return age;
}
private:
int age;
};
void test01() {
//对象实例化
person p(10);
cout << "人的年龄为:" << p.get_age() << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
按参数分:有参构造和无参构造(即构造时是否传参)(无参构造函数又称默认构造函数)
按类型分:普通构造和拷贝构造
//例如
class person {
public:
person() {
cout << "此为无参构造函数" << endl;
}
person(int a) {
cout << "此为有参构造函数" << endl;
age = a;
}
//拷贝构造
person(const person &p) {
age = p.age;//将传入的p的数据拷贝到此类中
cout << "此为拷贝构造函数" << endl;
}
~person() {
cout << "此为析构函数" << endl;
}
void set_age(int a)
{
age = a;
}
int get_age()
{
return age;
}
private:
int age;
};
括号法(常用)、显示法、隐式转换法
例如:
//函数的调用
void test01()
{
//括号法(类似于普通函数的调用)
person p1;//默认构造函数的调用
person p2(10);//有参构造函数的调用
person p3(p2);//拷贝构造函数的调用,传的参数是一个对象
//注意事项:默认构造函数的调用不用+()
//原因:因为若加(),则编译器会将其看成是函数的声明,person为返回值,p1为函数名称;
}
void test02()
{
//显示法
person p1;//默认构造函数调用
person p2 = person(10);//有参构造 等号右边称为匿名对象,等号左边的p2称为此匿名对象的名字
person p3 = person(p2);//拷贝构造
//person(10);//称为匿名对象 特点:当前行执行结束后,系统会立即回收掉匿名对象,而不是等test02结束再回收
//cout << "aaa" << endl;
//注:不要利用拷贝构造函数初始化匿名对象
//person(p3);//编译器会认为person(p3)==person p3;(即编译器会认为是对象声明,会出现重定义的错误)
}
void test03()
{
//隐式构造法
person p1 = 10;//等价于显示构造person p1=person(10); 有参构造
person p2 = p1;//拷贝构造
}
void test04()
{
//1、使用一个已经创建完毕的对象来初始化一个新的对象(即赋值操作时可以调用)
person p1(20);//有参调用
person p2(p1);//通过p1来对p2进行初始化
}
//2、值传递的方式给函数参数传值(传参时可以调用)
void func(person p)
{
person p1;//无参调用
p1.set_age(p.get_age());
cout << "p1的年龄为:" << p1.get_age() << endl;
}
//3、以值的方式返回局部对象(返回一个类时,会调用)
person func1()
{
person p1(10);//有参构造
return p1;//拷贝p1,使得p=p1,返回p
}
int main()
{
//test01();
//test02();
//person p1(18);//有参构造
//func(p1);//拷贝p1(拷贝构造)
person p = func1();
system("pause");
return 0;
}
1、若我们未在类中定义构造、析构函数(即没写构造、析构函数),则编译器会默认提供无参构造函数、析构函数、拷贝函数。
换句话说,我们虽然没有定义这三种函数,但我们依然可以调用这三种函数。
其中,无参构造函数和析构函数的函数体为空,拷贝函数函数体非空(编译器提供的代码作用为将类中对象属性拷贝到另一个对象中)
2、若我们定义了有参构造,未定义无参构造,则编译器不会提供无参构造,且我们无法调用无参构造;但编译器依然会提供拷贝函数
若我们定义了拷贝构造,但未定义无参构造和有参构造,则编译器不会提供无参构造和有参构造,即我们无法调用无参和有参构造
编译器默认做浅拷贝,仅是做简单的赋值操作,有一种情况的发生严重错误,需要用深拷贝解决
class person {
public:
person(int age,int height)//有参调用
{
m_age = age;
//浅拷贝
m_height = new int(height);//在堆区创建一个4字节的整形空间记录height的值,并用m_height接收
}
~person()//析构函数
{
if (m_height != NULL)
{
delete m_height;//删除m_height所指向的空间
m_height = NULL;
}
}
int m_age;
int* m_height;
};
void test01()
{
//运行以下代码将出现错误,原因为编译器进行的浅拷贝为将p1中的m_height=p.m_height;此时内存中有两个指针都指向了一个内存空间
//当test01函数结束时,触发析构函数,会先回收p1所指的空间,而p执行析构函数时,同样会回收其所指向的空间,而此空间已被释放
//因此p在执行析构函数时,造成内存访问越界的情况,导致程序崩溃,而要解决,则只需要自己实现一个深拷贝(函数)即可
person p(18, 160);
cout << "p的年龄为:" << p.m_age << " p的身高为:" << *(p.m_height) << endl;
person p1(p);//拷贝p的数据给p1;
cout << "p1的年龄为:" << p.m_age << " p1的身高为:" << *(p.m_height) << endl;
}
深拷贝:在堆区重新申请空间,进行拷贝操作
class person1 {
public:
person1(int age, int height)//有参调用
{
m_age = age;
m_height = new int(height);//在堆区创建一个4字节的整形空间记录height的值,并用m_height接收(height是一个指针)
}
~person1()//析构函数
{
if (m_height != NULL)
{
delete m_height;//删除m_height所指向的空间
m_height = NULL;
}
}
person1(const person1& p)//拷贝函数
{
m_age = p.m_age;
//m_height = p.m_height;浅拷贝代码
m_height = new int(*p.m_height);//此时new创建的内存空间中数据为int型数据而非浅拷贝时的int*
}
int m_age;
int* m_height;
};
void test02()
{
//此时能正常运行是由于,p1在拷贝p的数据时,创建了独立于p的另一个堆区的4B空间来存放height,因此两个对象进行析构时不会发生
//冲突
person1 p(18, 160);
cout << "p的年龄为:" << p.m_age << " p的身高为:" << *(p.m_height) << endl;
person1 p1(p);//拷贝p的数据给p1;
cout << "p1的年龄为:" << p.m_age << " p1的身高为:" << *(p.m_height) << endl;
}
c++除了提供了一般的构造函数和利用对象行为赋初值外,还提供了初始化列表来赋初值
语法:构造函数():属性1(值1),属性2(值2)...{};
class phone {
public:
phone() :name("华为"), MAC("FF-FF-FF-FF-FF-EE") {};//初始化列表
string name;//手机型号
string MAC;//手机MAC地址
};
//
int main()
{
phone p;
cout << "手机型号为:" << p.name << "\t" << "手机的MAC地址为:" << p.MAC << endl;
system("pause");
return 0;
}
//C++类A中的成员可以是另一个类B的对象,我们称该成员A为对象成员(即B类中有对象A作为成员,A为对象成员)
//构造的顺序是:先调用对象成员的构造,再调用本类的构造。而析构顺序与构造顺序相反
class A{};
class B {
A a;
};
静态成员就是在成员变量或成员函数之前加关键字static
静态成员分为静态成员变量和静态成员函数
静态成员变量:1、所有对象共享同一份数据。2、在编译阶段分配内存。3、需类内声明,类外初始化(必要)。
静态成员函数:1、所有对象共享同一个函数。2、静态成员函数只能访问静态变量
注:静态成员变量也是有访问权限的
静态成员函数不能访问非静态成员变量是因为所有对象共享同一静态成员函数,而静态成员函数无法区分所要访问的属性是哪个对象的属性
class man {
public:
static string name;//类内声明
static void func()
{
cout << "此为静态静态成员函数" << endl;
}
};
string man::name = "张三";//类外初始化
void test03()
{
//对象实例化
man m;
//m.name = "张三";//运行错误,因为name是静态成员变量,只能类外初始化
// 由于静态成员变量不属于某个对象,所有对象共享同一份数据,因此静态成员变量访问有两种方式
//通过对象进行访问
cout << m.name << endl;
//通过类名进行访问
cout << man::name << endl;
}
void test04()
{
//通过对象进行访问
man m;
m.func();
//通过类名进行访问
man::func();
}
int main()
{
test03();
//test04();
system("pause");
return 0;
}
当创建一个类为空时(即类中什么都没写),则编译器会为用该类创建的对象分配一个字节的空间,用以区分此对象。当类不为空时,则对象所占字节由类中非静态成员变量决定。
仅有成员变量是独属于该成员自身的。而每一个非静态成员函数都只会产生一份函数实例。即,多个同类型的对象会共用一块代码。而每个调用该函数的对象通过this指针来区分。this指针指向被调用的成员函数所属的对象。
this指针是隐含每一个非静态成员函数内的一种指针。this指针不需要定义,直接使用即可。
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中,返回对象本身,可使用return *this
class people {
public:
people(int age)
{
m_age = age;
}
//people+&-----使得返回的指针是p1对象本身,而非p1的拷贝副本
people& people_age_add(people &p)//类的行为
{
this->m_age += p.m_age;
return *this;//this指向了当前调用此函数的对象,而*this就是对象本身
}
int m_age;
};
void test05()
{
people p1(10);
//链式编程思想
p1.people_age_add(p1).people_age_add(p1).people_age_add(p1);
cout << p1.m_age << endl;
}
int main()
{
test05();
system("pause");
return 0;
}
常函数:1、成员函数后加const称为常函数。2、常函数内不可以修改成员属性。3、成员属性声明时加关键字mutable后,在常函数中仍可以修改
常对象:1、声明对象前加const称该对象为常对象。2、常对象只能调用常函数
class person2
{
public:
void func() const //常函数
{
//m_age = 100;//编译器报错,因为常函数内不可以修改成员属性
year = 100;//无语法错误,因为year前加了mutable关键字
}
void func1()
{
m_age = 100;//一般函数可以更改类中的属性
}
int m_age;
mutable int year;
string name;
};
void test06()
{
const person2 p;//常对象
//p.m_age = 100;//编译器报错,因为p是常对象,常对象内的属性不能更改
//p.func1();//编译器报错,因为func1是普通的函数,可以修改类中的属性。若常对象可以调用func1,相当于间接修改了对象中的属性,
//因此,常对象只能调用常函数,常函数中不能修改类中的属性
}
int main()
{
test06();
system("pause");
return 0;
}