c++成员操作
优点:将所有成员设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
#include<iostream>
#include<string>
using namespace std;
class Prosenal {
private:
string name;
int age;
string sex;
public:
//类设置私有好处之一,便于控制类的读写操作。
string getName()
{
return name;
}
void setName(string namea)
{
name = namea;
}
int getAge()
{
age = 0;
return age;
}
};
int main()
{
Prosenal p;
p.setName("佳佳");
cout << "名字是:" << p.getName() << endl;
cout << "年龄" << p.getAge() << endl;
system("pause");
return 0;
}
案例1:设计一个立方体类
#include<iostream>
using namespace std;
class Cude
{
private:
int m_h;
int m_w;
int m_l;
public:
void setH(int h)
{
m_h = h;
}
void setw(int w)
{
m_w = w;
}
void setl(int l)
{
m_l = l;
}
int getH()
{
return m_h;
}
int getw()
{
return m_w;
}
int getl()
{
return m_l;
}
int ca_C()
{
return 2 * m_l * m_w + 2 * m_w * m_h + 2 * m_h * m_l;
}
int ca_v()
{
return m_l * m_w * m_h;
}
};
bool iscude(Cude &c1,Cude &c2)
{
if (c1.getH() == c2.getH() && c1.getw() == c2.getw() && c1.getl() == c2.getl()) {
return true;
}
else {
return false;
}
}
int main()
{
Cude c1;
c1.setH(10);
c1.setw(10);
c1.setl(10);
cout << "面积是:" << c1.ca_C()<<endl;
cout << "体积是:" << c1.ca_v() << endl;
Cude c2;
c2.setH(10);
c2.setw(10);
c2.setl(11);
bool ref = iscude(c1, c2);
if (ref)
{
cout << "他们相等" << endl;
}
else {
cout << "他们不相等" << endl;
}
system("pause");
return 0;
}
对象初始化和清理
c++中的面向对象来源于生活,每个对象也都会有初始化设置以及对象销毁前的清理数据的设置
构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知的
同样的使用完一个对象或者变量,没有及时清理,也会造成一定的安全问题
c++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作,对象的初始化和清理工作是编译器强制要我们做的事情,因此如果不提供构造和析构,编译器会提供,编译器提供的构造函数和析构函数是空实现。
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作
构造函数语法
类名(){}
1.构造函数,没有返回值也不需要写void
2.构造函数与类名相同
3.构造函数可以有参数,因此可以发生函数重载
4.程序在调用对象时候会自动调用构造函数,无需手动调用,而且只会调用一次
析构函数语法
~类名(){}
1.析构函数,没有返回值也不需要写void
2.析构函数与类名相同,在名称前加上符号~
3.析构函数不可以有参数,因此不可以发生函数重载
4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;
class Preson {
//构造函数几个注意点
//1.没有返回值,不用写void
//2.构造函数和类名相同
//3.可以有参数,可以发生函数重载
//4.初始化前自动调用,无需手动调用,而且只会调用一次
public:
Preson()
{
cout << "构造函数被调用了" << endl;
}
//析构函数的几个注意事项
//1.没有返回值,不需要写void
//2.析构函数与类名相同
//3.没有参数,不可以发生函数重载
//4.对象销毁前自动调用,无需手动调用,而且只会调用一次
~Preson()
{
cout << "析构函数被调用了" << endl;
}
};
void test()
{
Preson p;
}
int main()
{
//test();//构造和析构函数都调用了。
Preson p;//在输出面板中可以看到只调用了构造函数,析构并没有调用,是因为,system("pause")这一行代码。当我们按任意键,可以看到析构函数的调用
system("pause");
return 0;
}
构造函数的分类及调用
两种分类方式
按参数分:有参构造和无参构造
按类型分为:普通构造和拷贝构造
三种调用方式:
括号法,当调用无参构造时,不要加小括号,要不然会创建不出来,原因是:编译器会把调用无参构成的语句认为成函数的声明
显示法
隐式转化法
#include<iostream>
using namespace std;
class Person2 {
public:
//构造函数的分类
//按参数分:有参构造和无参构造
//按构造类型分类:普通构造和拷贝构造
//无参构造(默认构造)
Person2()
{
cout << "无参构造函数调用" << endl;
}
Person2(int a)
{
cout << "有参构造函数调用" << endl;
age = a;
}
//一个简单拷贝构造,将另一个实例中的数据拷贝给调用类本身
Person2(const Person2& p)
{
age = p.age;
cout << "拷贝构造函数调用" << endl;
}
~Person2()
{
cout << "析构函数调用" << endl;
}
int age;
};
void test1()
{
//调用方式:
//1.括号法
//Person2 p;
//Person2 p1();//不能这样调用无参构造函数,编译器会认为这样是在声明函数
//Person2 p2(10);//构造函数重载,调用的是有参构造
//cout << "年龄:" << p2.age << endl;//10
//拷贝函数调用
//Person2 p3(p2);
//cout << "年龄:" << p3.age << endl;//10
//2.显示法
//Person2 p;
//Person2 p1 = Person2(10);//有参构造调用
//Person2 p2 = Person2(p1);
//cout << "年龄:" << p1.age << endl;//10
//cout << "年龄:" << p2.age << endl;//10
//Person2(10);//这个叫做匿名对象,特点,他在执行完之后系统会立即回收匿名对象
//cout << 111 << endl;
//输出顺序
//有参构造函数调用
//析构函数调用
//111
//不要用匿名对象使用拷贝构造函数,编译器会认为Person2(p2) === Person p2 ,认为是对象声明
//Person2(p5);可以用他实例新对象,前提是不能为前提实例的列如Person2(p2),p2前面就已经实例了,后面就不能在进行实例了
//p5.age = 100;
//cout << p5.age << endl;
//Person2(p2);//前面已经实例,编译器发生错误,p2重定义
//3.隐式转换法调用
Person2 p1 = 10;//他相当于Person p1 = Person2(10)
}
int main()
{
test1();
system("pause");
return 0;
}
拷贝构造函数调用时机
c++中拷贝构造函数调用时机通常有三种情况
使用一个已经创建完毕的对象来初始化一个新对象
值传递的方式给函数参数传值
以值方式返回局部对象
构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下
如果用户定义有参构造函数,c++不在提供默认无参构造函数,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不 会在提供其他构造函数
#include<iostream>
using namespace std;
class Person3 {
public:
//c++编译器给一个类中添加至少三个函数:默认构造函数,析构函数,拷贝构造函数
//如果我们给类自定义了个有参构造函数,那么,c++不会提供默认构造函数,但会给我们提供拷贝构造函数
//Person3()
//{
// cout << "无参构造函数调用了" << endl;
//}
//Person3(int A)//如果我们只写了有参构造函数,这时要注意,默认构造函数已经没有了,拷贝还在
//{
// cout << "有参构造函数调用了" << endl;
// m_Age = A;
//}
Person3(const Person3 &p)
{
m_Age = p.m_Age;
}
~Person3()
{
cout<<"析构函数调用了" << endl;
}
int m_Age;
};
//void test5()
//{
//Person3 p;//默认构造已经没有了
//Person3 p1(10);
//Person3 p2(p1);//拷贝c++还会提供
//cout << p1.m_Age << endl;
//cout << p2.m_Age << endl;
//}
void test6()
{
//当我们只写了拷贝构造函数,这时其他函数都不会存在
//Person3 p1;//报错
//Person3 p1(10);//报错
}
int main()
{
//test5();
test6();
system("pause");
return 0;
}
深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝带来的问题,堆区的内存重复释放
注意:如果属性有堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
#include<iostream>
using namespace std;
class Person
{
public:
//Person()
//{
// cout << "无参构造函数调用" << endl;
//}
Person(int age, int Height)
{
m_age = age;
m_Height = new int(Height);
cout << "有参构造函数调用" << endl;
}
Person(const Person &p)
{
m_age = p.m_age;
m_Height = new int(*p.m_Height);
cout << "拷贝构造函数调用" << endl;
}
~Person()
{
if (m_Height != NULL) {
delete m_Height;
m_Height = NULL;
}
cout << "析构函数调用" << endl;
}
int m_age;
int* m_Height;
};
void test()
{
//Person p;
Person p1(10, 180);
Person p2(p1);//编译器会默认提供一个浅拷贝的构造函数,本质就是,简单的赋值操作
cout << "年龄" << p1.m_age << endl;
cout << "身高" << *p1.m_Height << endl;
cout << "年龄" << p2.m_age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
初始化列表
作用:用于初始化属性
语法:构造函数():属性1(值1),属性2(值2)...{}
#include<iostream>
using namespace std;
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)
{
cout << "列表初始化方法赋初值" << endl;
}
int m_A;
int m_B;
int m_C;
};
void test1()
{
//Person p(10, 20, 30);
//cout << "m_A=" << p.m_A << endl;
//cout << "m_B=" << p.m_B << endl;
//cout << "m_C=" << p.m_C << endl;
Person p1(10, 20, 30);
cout << "m_A=" << p1.m_A << endl;
cout << "m_B=" << p1.m_B << endl;
cout << "m_C=" << p1.m_C << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
类对象作为类成员
c++类中的成员可以是另一个类的对象,我们称该成员为对象成员
#include<iostream>
#include<string>
using namespace std;
class Phone
{
public:
Phone(string phoneName) :phoneName(phoneName)
{
cout << "Phone初始化列表构造函数调用" << endl;
}
~Phone()
{
cout << "Phone析构函数调用" << endl;
}
string phoneName;
};
class Person
{
public:
string name;
Phone myPhone;//这个是Phone类
Person(string name, string phone):name(name), myPhone(phone){
cout << "Person初始化列表构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
void playInfo()
{
cout << name << "手机" <<myPhone.phoneName << endl;
}
};
void test01()
{
Person p("佳佳", "苹果十三");
p.playInfo();
}
int main()
{
test01();
system("pause");
return 0;
静态成员
静态成员就是在成员变量和成员函数前面加上关键字static,称为静态成员
静态成员分为
静态成员变量
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
静态成员变量可以通过对象进行访问
对象名.静态成员变量
还可以通过类名进行访问
类名::静态变量名
静态成员变量也是有访问权限的
#include<iostream>
using namespace std;
class Person
{
//静态成员变量
//编译阶段分配内存,类内声明,类外初始化,所有对象共享静态成员变量
public:
static int m_A;//类内声明
private:
static int m_B;//静态成员变量也是有权限的
};
//类外初始化
int Person::m_A = 100;
int Person::m_B = 200;
void test10()
{
Person p;
//通过对象进行访问静态成员变量
cout << p.m_A << endl;//100
//通过类名进行访问静态成员变量
cout << Person::m_A << endl;//100
}
void test110()
{
Person p;
//cout << p.m_B << endl;//因为m_B权限是私有的,外部并不能访问
}
int main()
{
//test10();
test110();
system("pause");
return 0;
}
静态成员函数
所有对象共享同一个函数
静态成员函数只能访问静态成员变量
两种调用方式
通过对象访问到静态成员函数
通过类名访问到静态成员函数
静态成员函数也是有访问权限的
#include<iostream>
using namespace std;
class Person
{
public:
//静态成员函数
//每个对象身上都会有一份
//静态成员函数只能访问静态成员变量,不能访问,其他成员变量或者函数,原因,当每次构造对象的时候,都会实例一个新的对象,静态成员函数不知道用那个对象身上的成员变量
static void fn()
{
cout << "我是静态成员函数" << m_C << endl;
//cout << "我是静态成员函数" <<m_D<< endl;//在静态成员函数内只能访问静态成员变量。不能访问非静态成员
}
static int m_C;
int m_D;
private:
static void fn1()//静态成员函数也有访问权限
{
cout << "我是静态成员函数1" << m_C << endl;
}
};
int Person::m_C = 100;
void test09()
{
Person p;
//第一种访问静态成员函数方式,通过对象方法
p.fn();
//第二种通过类名访问静态成员函数
Person::fn();
}
int main()
{
test09();
system("pause");
return 0;
}
c++对象模型和this指针
成员变量和成员函数分开存储
在c++中。类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
#include<iostream>
using namespace std;
class Person
{
public:
int m_a; //非静态成员变量,属于类上
int m_c;//非静态成员变量,属于类上
float m_d;//非静态成员变量,属于类上
static int m_b;//静态成员变量,不属于类上
void fn()//非静态成员函数,但不属于类上
{
cout << "hello world" << endl;
}
static void fn1()//静态成员函数,但不属于类上
{
cout << "hello world" << endl;
}
};
int Person::m_b = 100;
void test08()
{
Person p;
//cout << "sizeof = " << sizeof(p) << endl;//如果是一个空类的话,他的内存空间为1个字节
cout << "sizeof = " << sizeof(p) << endl;//只有非静态成员变量才属于类的对象上,内存占用大小,非静态成员之间相加
}
int main()
{
test08();
system("pause");
return 0;
}
this指针概念
通过上文可知,成员变量和成员函数是分开存储的,每一行非静态成员函数只诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针解决上述问题,this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一中指针
this指针不需要定义,直接使用即可
this指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this
#include<iostream>
using namespace std;
class Person
{
public:
int m_Age;
int m_Height;
Person(int age,int height)
{
//关于c++this指向问题,this指针指向被调用的成员函数所属的对象,通俗来说谁调用就指向谁,
//this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。
this->m_Age = age;//防止非静态成员变量重名。
this->m_Height = height;
this->fn();//在初始化变量同时,通过this调用fn函数
//this->m_A = 1000;
}
void fn()
{
this->m_A = 10000;
this->m_Height = 1500;
this->fn1();
cout << "hello world" << endl;
}
static void fn1()
{
cout << "hello world1" << endl;
}
static int m_A;
};
int main()
{
Person p(10,100);
cout << p.m_Age <<"\t" << p.m_Height << endl;
cout << p.m_A << endl;
system("pause");
return 0;
}