类
类(class)和结构体(strct)的区别是
类默认权限是私有权限
结构体默认权限是公有
封装
封装的意义
将属性和行为作为一个整体,表现生活中的事物
将属性和行为加以权限控制
构造函数
只要创建一个类 c++编译器就会给每个类创建至少四个构造函数
1.默认构造函数(空实现)
2.析构函数(空实现)
3. 拷贝构造函数(值拷贝) 这个编译器自己创建的拷贝构造函数就是该相应值的拷贝
4. 赋值运算符operator=对属性进行值拷贝
5.被const修饰的取地址操作符重载
如果我们写了有参构造函数 编译器就不再提供默认构造函数 依然提供拷贝构造函数 所以此时如果使用无参创建对象 Stu a;就会报错 因为编译器也没有提供默认构造函数 你也不提供无参构造函数
如果我们写了拷贝构造函数 编译器就不再给我们提供其他构造函数了
构造函数不能是const型 因为构造函数本意就是用来赋值初始化的
拷贝构造函数
class Stu{
private:
int age;
Stu()
{
cout<<“默认构造函数”;
}
Stu(const Stu& p)//这个构造函数就是专门用来拷贝值 形参用了const 他的值不能改变
{
age = p.age;
cout<<“拷贝构造函数”
}
}
使用一个已经创建完毕的对象初始化一个新的对象时会调用拷贝构造函数
Stu a(10);
Stu b(a);//这样就是把对象a里面的值在b创建的时候就传给了b 此时会调用拷贝构造函数
值传递的时候也会调用
/就是在调用这个函数的时候 实际上是创建了一个形参 然后就大致做了一个 Stu s = Stu(a)这个操作 所以也调用了拷贝构造函数/
void do(Stu s)//这是一个函数
{
}
Stu a;
do(a);
值方式返回局部对象也会调用
Stu do()//这是一个函数
{
Stu a;
return a;//这个情况和上面的基本意义
}
记得在写派生类的拷贝函数时,调用基类的拷贝函数拷贝基类的部分,不能忘记了。
深拷贝与浅拷贝问题
深拷贝:在堆区重新申请空间 进行拷贝操作(实际上就是自己再实现一个拷贝构造函数 )
浅拷贝:简单的赋值拷贝操作
利用编译器提供的拷贝构造函数 会做浅拷贝操作
浅拷贝会出现的问题(解决方法在代码里)
堆区内存重复释放
class Stu
{
private:
int num;
int* age;
public:
Stu(int nu,int ag)//含参数的构造函数
{
num = nu;
age = new int(ag);//把传进来的年龄放在堆区 并且把堆区的地址传给age
};
~Stu()
{
if (age!= NULL)
{
/*原因是 a.age存储的是ag在堆内存中的地址 而b.age是通过默认的*/
/*拷贝函数直接原封不动的拷贝了a.age 然后在结束的时候 */
/*由于栈区的 先进后出规则 先调用b的析构函数 此时ag的堆内存已经*/
/*被释放了 再调用a的析构函数时 就会出现重复释放问题了 */
delete age;//在这里就会出现重复释放的问题
age = NULL;//防止野指针的出现
}
}
/*解决办法*/
/*自己实现拷贝构造函数 解决浅拷贝带来的问题*/
Stu(const Stu& p)
{
num = p.num;
//age = p.age;编译器默认实现的拷贝构造函数就是这行代码
//深拷贝
age = new int(*p.age);
}
};
int main()
{
Stu a(1, 2);
Stu b(a);//这里就用了编译器默认的拷贝函数
}
初始化列表(用来初始化属性)
语法
构造函数():属性1(值1),属性2(值2).。。。()
例子
class Stu
{
private:
int num;
int age;
int sex;
int love;
public:
Stu():num(1),age(2),sex(3),love(4)
{
}
Stu(int a,int b,int c,int d):num(a),age(b),sex(c),love(d)
{
}
};
静态
静态成员变量
所有对象共享同一份数据 就是直接可以使用int b = Stu::age 也可也在类对象中调用 Stu a; int b = a.age;
在编译阶段分配内存
类内声明 类外初始化
就是只能在类内声明了 然后在类外来初始化 例如 int Stu::age = 0; 他不能放在函数里面 包括main函数 也不能放在类里面初始化
还有一种就是放在该类的静态成员函数中来赋值
静态成员函数
所有对象共享同一个函数
就是不需要对象也可以直接调用 例如 Stu::func();
静态成员函数只能访问静态成员变量
静态成员变量可以在该函数中赋值
因为静态成员函数是被该类的所有成员共用 而且静态成员函数是可以通过Stu::func();使用的 所以 如果在静态成员函数中对非静态成员变量进行赋值的时候 就容易产生不知道是给哪个对象的该成员变量赋值
this指针
this指针本质是指针常量 Stu *const this 指向的是调用他的对象 this的指向是不能修改的
如果调用 return *this 则返回的就是自身对象
常函数和常对象
常函数
/*在成员函数后面加const 修饰的是this指针 让指针指向的值也不能修改*/
void func() const //这样加const等价于 const Stu * const this
{
this->num = 100;//这样就会报错
this->age = 100;//加了mutable 在哪里都是可以修改的
}
int num;
mutable int age;
常对象
const Stu a;
常对象不能修改类内的属性
常对象只能调用常函数
注意事项
1.如果在创建类对象的适合加一个空括号 编译器并不会调用无参的构造函数 而是把该语句看成一个函数的声明 例如
class Stu{ Stu{这里是无参的构造参数} };
Stu a(); //会被看成一个返回类型为stu的函数的声明 不会认为在创建对象
2.Stu a = Stu(10);这个约等于 Stu a(10);
直接写的 Stu(10);这个是匿名对象 特点:当前行执行结束后 系统会立即回收掉匿名对象
3.不要用拷贝构造函数 初始化匿名对象
例如 Stu a;Stu(a); 编译器会认为Stu(a) 等价于 Stu a; 这样就等于重定义了
4.Stu(a) = 10;等价于 Stu a = Stu(10); 等价于 Stu a(10);
5.类对象可以作为类成员(当其他类对象作为本类成员,构造时 先构造类对象 再构造自身 先析构自身 再析构类对象)
空对象占用的内存空间是1(c++编译器会给每一个空对象分配一个字节空间 主要因为每一个对象都需要占一个独一无二的空间 如果你给空对象分配了一个字节空间 那么在创建多个空对象的时候就会出现多个对象在一个空间的问题)
友元的三种实现方式
全局函数做友元
class Building
{
friend void func(Building building);//这里就是一个Building类的全局友元函数 它可以访问该类中的所有属性和方法 不用一定写在public下面
public:
private:
}
类做友元(目的是一个类可以访问另一个类的私有成员)
class Building
{
friend class GoodGay;//这里就是一个Building类的友元类 它可以访问该类中的所有属性和方法 不用一定写在public下面
public:
private:
}
成员函数做友元
class Building
{
friend void GoodGay::visit();//这里就是一个Building类的友元GoodGay类成员函数 它可以访问该类中的所有属性和方法 不用一定写在public下面
public:
private:
}