C++ Study Notes

本文详细探讨了C++中的浅拷贝与深拷贝的区别,构造函数与析构函数的作用,以及如何通过运算符重载实现对象的自定义操作。还涵盖了友元、空对象、空指针、常函数和对象模型等内容,帮助读者掌握关键概念和实践技巧。
摘要由CSDN通过智能技术生成
->
浅拷贝带来的问题是堆区的内存重复释放

->
成员属性设置为私有
可以自己控制操作属性的权限

->
构造函数
可以有参数可以重载
没有返回值也不用写 void
类名(){}
创建对象的时候系统自动调用且只调用一次
有参构造、无参构造
拷贝构造函数

析构函数
不能有参数,不发生重载
~类名(){}
对象在销毁前会自动调用析构且只调用一次

->
如果用户定义了有参构造,c++不再提供无参构造,还是会提供拷贝构造 
如果用户定义了拷贝构造,c++不提供其他构造函数

->
浅拷贝是简单的赋值
c++默认的拷贝构造函数默认是浅拷贝

深拷贝再堆区重新申请空间,进行拷贝操作

->
c++类中的成员可以是另一个类的对象
称为 对象成员

class A{}
class B
{
    A a;
}

构造的时候先A后B
"先有砖头后有房子"
"先造零件后装机"

析构的时候先B后A
"拆房子,房子先没,砖后没"
"先拆机后扔零件"

->
静态成员
    所有对象共享同一份数据
    编译阶段分配内存
    类内声明、类外初始化

静态成员函数
    所有对象共享同一个函数
    静态成员函数智能访问静态成员变量

    静态成员变量不属于某个对象,所有对象共享同一份数据
    静态成员变量有两种访问方式
        1.通过对象访问  Person p1; p1.m_a;
        2.通过类名访问  Person::m_a

    静态成员函数也是同样的两种访问方式

->
对象模型和this指针

this指针的本质是一个指针常量,指向不可修改,指向的值可以修改(非const变量)

c++中:
    类内的成员和成员函数分开存储
    只有非静态成员变量才属于类的对象上

this指针指向被调用的成员函数所属的对象
this是隐含每一个非静态成员函数内的一种指针

当形参和成员变量同名的时候,可以用this指针来区分
在类的非静态成员函数中返回对象本身,return *this

->
空对象占用内存空间为 1
c++编译器会给每个空对象也分配 1 字节的空间,是为了区分对象占内存的位置
每个空对象也应该有一个独一无二的内存地址

->
Person& PersonAddAge(const Person &p)
    {
        this->age+=p.age;
        cout<<this->age<<endl;
        // 返回本体
        return *this;
    }

    Person p1(10);
    Person p2(10);
    p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);

    如果函数返回的不是Person的引用,能进行加法,但p1.age还是20,因为会调用拷贝构造函数
    返回一个和p1一样的对象但不是p1

    不加引用可以:
    p3=p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);

    这样p3.age=40

->
空指针调用成员函数
class Person
{
public:
    void showClassName()
    {
        cout<<"this is Person class."<<endl;
    }
    void showPersonAge()
    {
        cout<<"age = "<<m_age<<endl;
    }

    int m_age;
};

Person *p=NULL;
p->showClassName();             //不会报错,因为函数内部没有访问类的属性
p->showPersonAge();             //报错

void showPersonAge()
{
    cout<<"age = "<<m_age<<endl;
    //相当于cout<<"age = "<<this->m_age<<endl;
    因为p是一个空指针,所以会报错
}

if (this==NULL)
{
    return;
}
加上一个判断防止代码崩溃

->
常函数:
    成员函数后加const后我们称为这个函数为常函数
    常函数内不可以修改成员属性
    成员属性声明时加关键字mutable后,在常函数中依然可以修改

    void showPerson() const         //相当于 const Person *this;
    {
        this->m_age=100;   //报错,加了const,值不可修改
    }

常对象:
    声明对象前加const称该对象为常对象
    常对象只能调用常函数

    const Person p;

->
友元
在类中声明一个函数,前面加上friend关键字
那么这个函数也能访问类中private中的私有成员

全局函数做友元:
class Buliding
{
    friend void goodGay(Buliding &building);
public:
    string m_SittingRoom;

private:
    string m_BedRoom;

public:
    Buliding()
    {
        this->m_BedRoom="BedRoom";
        this->m_SittingRoom="SittingRoom";
    }

};

void goodGay(Buliding &building)
{
    cout<<"calling "<<building.m_SittingRoom<<endl;

    cout<<"calling "<<building.m_BedRoom<<endl;
}

成员函数做友元:
一个类的成员函数可以访问另一个类的私有成员
在类中加上
friend void 类名::函数名;


类做友元:
一个类能访问另一个类的私有成员
在类中加上
friend class 类名;
就可以了

->
运算符重载
实现自定义数据类型的运算

Person PersonAddAge(Person &p)
{
    Person tmp;
    tmp.m_a=this->m_a+p.m_a;
    return tmp;
}

p3=p1.PersonAddAge(p2)

运算符重载
成员函数重载:
Person operator+ (Person &p)
{
    Person tmp;
    tmp.m_a=this->m_a+p.m_a;
    return tmp;   
}
可以简化为
p3=p1+p2
也可以
p3=p1.operator+(p2)

全局函数重载:
Person operator+ (Person &p1,Person &p2)
{
    Person tmp;
    tmp.m_a=this->m_a+p.m_a;
    return tmp;  
}


->
左移运算符重载 <<
cout<<a<<endl;

现在有一个Person p;
想要直接 cout<<p<<endl;
就可以输出p的所有属性: 重载左移运算符

如果用成员函数重载:
    void operator<<(cout){}   相当于 p.operator<<(cout)  ->  p<<cout
    结果显然不对
所以重载左移运算符不会用成员函数重载,因为无法实现cout在左侧
只能利用全局函数重载
void operator<<(ostream &cout,Person &p)         //本质 operator<<(cout,p)  -> cout<<p
{
    //参数改为out,a,c都没问题,因为ostream对象只能有一个
    cout<<"m_a = "<<p.m_a<<" m_b = "<<p.m_b<<endl;

}
cout<<p;

cout后面可以跟很多个<<,链式一直进行下去
所以函数的返回值要是cout本身,ostream & operator<<(ostream &cout,Person &p);

因为一般把类的属性设置为private,所以<<运算符重载可以设置为友元

->递增运算符++
++可以在左边也可以在右边
++a a++
两种都可以重载
++a: 前置递增
    MyInteger & operator++()
    {
        this->m_num++;
        return *this;
    }
a++: 后置递增
注意不返回引用,因为temp是局部变量
函数执行完就被释放了,return之后就没了,后面都是非法操作
    MyInteger operator++(int) 
    {
        //先记录当前的值
        MyInteger tmp=*this;
        //后递增
        this->m_num++;
        //返回记录结果(值)
        return temp;
    }

在重载的函数体内部不需要区分前置后置,this->m_num++ / ++this->m_num  都可以

->
重载赋值运算符 =
重载赋值运算符可以避免 p2=p1 带来的浅拷贝问题

考虑到a=b=c语法的存在
所以返回值也是对象自身Person &
Person& operator=(Person &p);

->
重载 == 
a==b 看形式相当于a调用==,所以函数参数是b
显然关系函数的返回值类型是bool
所以:
    bool operator==(Person &p);
    函数体内部自定义判断的规则

其他的关系运算符类似

->
函数调用运算符重载 ()  小括号
由于重载后的使用方式非常像函数的调用,因此称为 仿函数
仿函数没有固定写法,非常灵活
在一个类中重载小括号
class MyPrint
{
public:
    void operator()(string text);
};

void MyPrint::operator()(string text)
{
    cout<<text<<endl;
}

void test01()
{
    MyPrint myprint;
    myprint("hello world");
}
类名后面加小括号直接调用

仿函数非常灵活没有固定的写法,参数也可以很多个

使用的时候也不需要创建实例对象
可以MyPrint()("hello world");    // 匿名对象,执行完了这一行就会被释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值