学构造函数的这辈子有了

拷贝构造函数

1.拷贝构造函数,是一个特殊的构造函数。系统默认提供了。但是可以手动定义。

2.功能:使用一个类对象给另一个类对象初始化时,会自动调用拷贝构造函数

3.定义格式

bash 复制代码
1、没有返回值
2、函数名与类同名
3、参数:该类的其他对象的引用
4、访问权限:一般为public
5、定义格式:类名 (类名 &other)

调用时机:

使用一个类对象给另一个初始化时:

string s1("hello world"); //调用有参构造

string s2 = s1; //调用拷贝构造

string s3(s1); //调用拷贝构造

使用值传递时实参取代形参时调用拷贝构造函数。

使用值返回时返回结果也是调用拷贝构造。

深浅拷贝问题:

如果类中有指针成员并且指向其它空间时,应调用深拷贝

使用浅拷贝就会出现多个对象中指针成员指向同一个内存空间,析构指针空间时,会造成对同一块内存空间的多次释放的情况,造成段错误(double free),此时就需要深拷贝。

深拷贝需要程序手动定义拷贝构造函数,在拷贝构造函数的初始化列表中,给新对象的指针成员重新分配空间,将原来对象指针空间指向的内容拷贝到该空间中即可完成深拷贝

cpp 复制代码
Stu(Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::深拷贝构造函数"<<endl;
    }

移动构造函数

如果类中不显性定义拷贝构造函数或者移动构造函数,那么系统会自动提供这两个函数,如果程序员手动定义了其中任意一个函数,系统就两个都不提供了,如果需要使用,则需要程序员自己手动定义

移动构造函数,是将临时空间或将亡空间,托管给一个新的对象。

bash 复制代码
1、没有返回值
2、函数名与类同名
3、参数:该类的其他对象的右值引用
4、访问权限:一般为public
5、定义格式:类名 (类名 &&other)
cpp 复制代码
//自定义移动构造函数
    Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::移动构造函数"<<endl;
    }
     
    //调用移动构造函数
    Stu s3 = move(s1);

取地址运算符重载函数

功能:取得当前类对象的地址

定义格式

bash 复制代码
1、返回值:当前类的指针
2、参数:无
3、权限:一般为public
4、格式: 类名 * operator&()
cpp 复制代码
    //自定义取地址运算符重载函数
    Stu * operator&()
    {
        cout<<"取地址运算符重载函数"<<endl;
        return this;
    }

    //取地址运算符重载函数
    &s1;

一个空类中默认提供的特殊成员函数

cpp 复制代码
class Temp
{
    public:
        Temp(){}                   //无参构造
        Temp(const Temp &other){}     //拷贝构造
        ~Temp(){}                        //析构函数
        Temp * operator&(){return this;}     //取地址运算符重载函数
        Temp(const Temp &&other){}     //移动构造
        Temp &operator=(const Temp& other){}      //拷贝赋值函数
        Temp &operator=(const Temp&& other){}      //移动赋值函            
};

匿名对象

匿名对象就是没有名字的是对象,其生命周期就在调用的语句内,可以理解成一个将亡值。

定义时调用构造函数。

使用场景

1、使用匿名对象给一个新定义的对象进行初始化

2、使用匿名对象给数组进行初始化

3、使用匿名对象作为函数参数传递

cpp 复制代码
    Stu("lisi", 20);             //定义一个匿名对象
    cout<<"***********************************"<<endl;

    //匿名对象使用方式1:给新的对象进行初始化工作
    Stu s2 = Stu("wangwu", 30);           //依赖移动构造

    cout<<"***********************************"<<endl;
    //匿名对象使用方式2:给对象数组进行初始化
    Stu s[3] = {Stu("zhaoliu", 30), Stu("sunqi", 30), Stu("zhouri", 30)};
    cout<<"***********************************"<<endl;

    //匿名对象使用方式3:作为函数的形参进行传递
    fun(Stu("wukong", 500));
    cout<<"***********************************"<<endl;

友元

可以在其他类中访问当前类的所有信息,友元可以无条件访问该类中的所有权限下的成员。

友元分为友元函数和友元类

cpp 复制代码
class Stu;                  //对类的前置声明
void  fun(Stu s);  

//类中声明一个全局函数作为友元函数
friend void  fun(Stu s);

//定义一个全局函数
void  fun(Stu s)
{
    s.show();           //在类外可以正常调用类中的公共权限下的成员
    cout<<"name = "<<s.name<<endl;       //友元函数可以访问对应类中的所有权限下的成员
    cout<<"age = "<<s.age<<endl;        //友元函数可以访问对应类中的所有权限下的成员
}

 Stu s1("zhangsan", 18);
 fun(s1);//调用
cpp 复制代码
#include <iostream>
using namespace std;
class Dog;
class Cat
{
private:
    int count;
    double weight;
public:
    Cat(){cout<<"无参构造"<<endl;}
    Cat (int c,double w):count(c),weight(w){cout<<"有参构造"<<endl;}
    ~Cat(){cout<<"析构函数"<<endl;}
    friend void sum(Cat &c,Dog &d);
};

class Dog
{
private:
    int count;
    double weight;
public:
    Dog(){cout<<"无参构造"<<endl;}
    Dog (int c,double w):count(c),weight(w){cout<<"有参构造"<<endl;}
    ~Dog(){cout<<"析构函数"<<endl;}
    friend void sum(Cat &c,Dog &d);
};

void sum(Cat &c,Dog &d)
{
    cout<<"猫总个数"<<c.count<<endl;
    cout<<"猫总重量"<<c.weight*c.count<<endl;
    cout<<"狗总个数"<<d.count<<endl;
    cout<<"狗总重量"<<d.weight*d.count<<endl;
}

int main ()
{
    Cat cat (5,1.50);
    Dog dog (6,2.00);
    sum (cat,dog);
    return 0;
}

友元类

声明一个A类为B类的友元类,则B允许A访问其所有权限下的成员包括私有成员

声明格式:friend 类名;

cpp 复制代码
class teacher
{
    //定义成员函数
    void display(Stu s);          //只能类内声明,类外定义
}

  
//声明老师类为友元类
friend class Teacher;

友元的总结

1> 不到万不得已的情况下,不要使用友元,因为友元的出现使得封装称为虚谈,友元破坏了类的封装性

2> 友元不具有传递性:A是B的朋友,B不一定是A的朋友

3> 友元不具有传递性:A是B的朋友,B是C的朋友,A不一定是C的朋友

4> 友元不具有继承性:父类的朋友,不一定是子类的朋友

5> 必须使用友元的情况:插入和提取运算符重载时,只能使用友元函数来解决

常成员(const)

引入常成员可以保护成员函数和成员变量、常对象

成员函数仅仅对成员变量有只读权限,不需要进行更改,那么此时就需要对成员函数进行const,达到保护成员变量的功能。

在传递某个对象时,对方仅仅只是对该对象有只读权限,也需要使用常成员,进一步保护成员变量

常成员函数

1> 定义格式:定义成员函数时,在括号后加关键字 const

返回值类型 函数名(形参列表) const

2> 作用:在常成员函数中,是不允许更改成员变量的值

3> 类中常成员函数与同名的非 常成员函数构成重载关系,原因是,形参this的类型不同

非常成员函数中形参this:类名 * const this;

常成员函数中的形参this: 类名 const * const this;

4> 非 常对象,优先调用非常成员函数,如果没有该非常成员函数,那么就会调用同名的常成员函数

常对象

1> 定义格式:const 类名 对象名;

2> 常对象只能调用常成员函数,不能调用非常成员函数

mutable关键字

1> 使用该关键字修饰的成员变量,可以解除常属性,即使在常成员函数中也能更改该变量的值

cpp 复制代码
  //定义成员函数
   void show()const        // 类名 const  * const this;

常函数

1> 格式:const 返回值类型 函数名(形参列表){函数体内容}

2> 功能:常函数是保护函数的返回值不被修改的

cpp 复制代码
//引用函数,const修饰的函数称为常函数
const int &fun()
{
    static int num = 520;
    return num;
}

关于C/C++中const的使用

1> const修饰普通变量,表示定义的是一个常变量,该变量只可读不可写,定义时必须初始化

2> const修饰指针变量时,如果放在*前表示,指针所执行空间中的内容可读不可写

cpp 复制代码
const int *p = &data; // p 指向的是 const int, 所以 *p 里的内容不能修改但是可以改变指向

如果放在*后,表示指针的值可读不可写,也就是指针指向不能改变

cpp 复制代码
int *const p = &data; // p 是 const 指针, 所以 p 不能更换指向

如果两边都加,表示都不可以更改,都是可读不可写,包含指向和值

3> const修饰函数的形参,表示传过来的数据,可读不可写

4> const修饰函数返回值时,表示保护函数的返回结果不被修改(指针函数和引用函数)

5> const修饰常成员函数,放到成员函数最后面,表示保护成员变量不被修改

相关推荐
学会沉淀。7 分钟前
Docker学习
java·开发语言·学习
ragnwang24 分钟前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
西猫雷婶37 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila38 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城1 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
码银3 小时前
【python】银行客户流失预测预处理部分,独热编码·标签编码·数据离散化处理·数据筛选·数据分割
开发语言·python
从善若水3 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
lqqjuly3 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++