C/C++的学习笔记(C++中 面向类和对象 编程思维)

12 篇文章 0 订阅

C/C++的学习

一.总结:

      C语言的应用十分广泛、灵活性强,同时它也比较基础,很适合一些刚入门的
  小白学习,若有了C语言的基础,再去学习其他的语言也会轻松很多。我也是一名
  小白,真正入门技术也才半年的时间,这半年的时间里我学习了很多关于技术方面
  的知识,也自己尝试或与他人合作设计完成过一些小的项目(日后也会更新到我的
  博客中)......
      这段时间,我主要在学习C++,由于C语言的基础部分和C++的极其类似,所以
  我在学习C++的同时,相当于复习了一遍C语言。我也重新整理了一份笔记,希望我
  也可以给大家带来一点帮助。
      笔记中有错误的地方,欢迎指出,欢迎大家讨论!

C++ 面向类和对象 编程思维

类和对象:

C++面向对象有三大特性:封装继承多态

封装:

1.将属性和行为作为一个整体,用来表现生活中的事物;

2.将属性和行为加以权限控制;

封装意义一:在设计类的时候,属性和行为写在一起,一起用来表现事物;

语法:

class 类名
{
	访问权限
	行为
	属性
}

案例:设计一个圆类,并求圆的周长

//案例:设计一个圆类,并求圆的周长

class Circle  //圆类
{
public:  //访问权限(公共权限)
    
    double caclucateZC()  //行为(用函数表示行为),这里的行为是计算周长
    {
        return 2 * PI * m_r;
    }
    
    int m_r;  //属性(用变量表示属性),这里的案例只需要半径这个属性
    
};

int main()
{
    Circle C1;  //实例化(通过一个类创建一个对象的过程,这里c1就是一个对象)
    
    c1.m_r = 10;  //给对象的属性赋值
    
    cout << c1.calculataZC() << endl;  //调用对象C1中的行为
}

类中的属性和行为,统一称为成员;属性也被称为成员属性或成员变量;行为也被成为成员函数或成员方法;


封装意义二:类在设计时,可以把属性和行为放在不同权限下,加以控制;

访问权限:

public:公共权限(成员类内外都可以访问)

protected:保护权限(成员类内可以访问,成员类外不可以访问;且儿子可以访问父亲中的保护内容)

private:私有权限(成员类内可以访问,成员类外不可以访问;且儿子不可以访问父亲中的私有内容)


structclass 的唯一区别就是:默认访问权限的不同;

struct 的默认访问权限是:public 公共权限;

class 的默认访问权限是:private 私有权限;


成员属性设为私有的优点:

1.将所有成员属性设为私有,可以自己控制读写权限;

2.对于写权限,我们可以检测数据的有效性;

一般情况下,成员属性被设为私有后,可以在行为那里提供对外的接口;(而行为的权限一般设为:公共权限)

把类分文件编写:.h 放类中行为和属性的声明;.cpp 放类中行为的实现;

//假设有一个点类,属性有横坐标和纵坐标,行为有设置点的横坐标和纵坐标

//未分文见编写前:
class Point
{
public:
    
    void SetX(int X)  //设置点的横坐标
    {
        m_x = X;
    }
    void SetY(int Y)  //设置点的纵坐标
    {
        m_y = Y;
    }
    
    int m_x;  //点的横坐标
    int m_y;  //点的纵坐标
    
};

//分文件编写后:
//.h文件:放类中行为和属性的声明
class Point
{
public:
    
    void SetX(int X);  //设置点的横坐标
    void SetY(int Y);  //设置点的纵坐标
    
    int m_x;  //点的横坐标
    int m_y;  //点的纵坐标
    
};

//.cpp文件:放类中行为的实现,同时要加上作用域
void Point::SetX(int X)  //设置点的横坐标
{
    m_x = X;
}
void Point::SetY(int Y)  //设置点的纵坐标
{
    m_y = Y;
}

//最后一般会有三个文件:main.cpp、Point.cpp、Point.h,而在main.cpp和Point.cpp中要 #include "Point.h"

对象的初始化和清理:构造函数和析构函数

构造函数和析构函数编译器会自动调用,无需手动调用;这两个函数都是必须实现的函数,如果我们没有提供,编译器会提供一共空实现的构造函数和析构函数;

构造函数语法:

类名()
{
	....
}

注意事项

1.构造函数没有返回值,也不用 void

2.构造函数的名称必须与类名相同;

3.构造函数可以有参数,因此可以发生函数重载;

4.程序在创建对象时,会自动调用构造函数,无须手动调用,而且只会在创建对象的时候调用一次;


析构函数语法:

~类名()
{
	....
}

注意事项
1.析构函数没有返回值,也不用 void

2.析构函数的名称必须与类名相同,并且在函数名称前面加上符号~;

3.析构函数不可以有参数,因此不可以发生函数重载;

4.程序在对象销毁时,会自动调用析构函数,无须手动调用,而且只会在对象销毁的时候调用一次;


构造函数的分类和调用:

两种分类方式

1.按参数分:I.无参构造(默认构造);II.有参构造;

2.按类型分:I.普通构造(除了拷贝构造以外的构造函数都可以称为普通构造);II.拷贝构造;

三种调用方式:1.括号法;2.显示法;3.隐式转换法

注意事项

1.调用无参构造函数的时候,不要加();

2.不要利用拷贝构造函数初始化匿名对象;

//假设已存在一个类Person

//分类方式:无参构造(默认构造)、有参构造、拷贝构造

Person()  //无参构造(默认构造)
{
    ....
}

Person(int a)  //有参构造
{
    ....
}

Person(const Person & p)  //拷贝构造,相当于我复制了一份一模一样的Person
{                         //将这个传进来的Person(即p)的所有属性拷到我这个Person上来
    ....
}

//调用方式:括号法、显示法、隐式转换法

//括号法(常用):
Person p1;  //调用无参构造(默认构造)
Person p2(10);  //调用有参构造
Person p3(p2);  //调用拷贝构造

//显示法:
Person p2 = Person(10);  //调用有参构造
Person p3 = Person(p2);  //调用拷贝构造

//隐式转换法:
Person p2 = 10;  //转换成 Person p2 = Person(10);
Person p3 = p2;  //转换成 Person p3 = Person(p2);

拷贝构造函数调用时机:

三种情况:

1.使用一个已创建完毕的对象来初始化一个新对象;(最常用的情况)

2.值传递的方式给函数参数传值;

3.以值传递的方式返回局部对象;


构造函数调用规则:

默认情况下,C++编译器至少给一个类添加了三个函数:

1.默认构造函数(无参,函数体为空);

2.默认析构函数(无参,函数体为空);

3.默认拷贝构造函数,对属性进行值拷贝(这里默认提供的拷贝构造函数做的是浅拷贝);

调用规则:

1.如果用户定义了有参构造函数,则编译器不再提供默认构造函数,但是还是会提供默认拷贝构造函数;

2.如果用户定义了拷贝构造函数,则编译器不再提供其他构造函数;

3.析构函数与构造函数无关,编译器仍然会提供默认的析构函数(除非自己写了新的析构函数);

总结:如果按照 无参构造->有参构造->拷贝构造 的顺序从上往下看,则我们提供了其中某个位置的构造函数,那它上面的函数编译器不再提供,下面的函数编译器还是会提供;


深拷贝与浅拷贝(面试经典问题)

浅拷贝:简单的赋值拷贝操作(编译器给我们做的简单拷贝工作);

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

析构代码,作用就是将堆区开辟的数据做释放操作,而浅拷贝带来的问题就是堆区的内存重复释放,浅拷贝带来的问题需要靠深拷贝来解决;如果有属性在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题;

//假设有一个人类,他的属性有年龄和身高

class Person
{
public:
    
    Person()  //无参构造
    {
        cout << "Person 无参构造函数的调用" << endl;
    }
    
    Person(int age,int height)  //有参构造
    {
        cout << "Person 有参构造函数的调用" << endl;
        m_Age = age;
        m_Heigjt = new int(height);
    }
    
    Person(const Person & p)  //拷贝构造
    {
        cout << "Person 拷贝构造函数的调用" << endl;
        m_Age = p.m_Age;  
        m_Height = p.m_Height;  //浅拷贝(编译器给我们做的简单拷贝工作)
        m_Heigjt = new int(*p.m_Height);  //深拷贝
    }
    
    ~Person()  //析构函数,将堆区开辟的数据释放
    {
        if(m_Height!=NULL)
        {
            delete m_Height;
            m_Height = NULL;
        }
    }
    
    int m_Age;
    int * m_Height;
};

void test_1()  //测试函数
{
    Person p1(18,175);
    Person p2(p1);
}

根据上面的代码,如果我们在 main 函数里调用了测试函数,它会创建两个对象(创建第一个对象时,调用有参构造;创建第二个对象时,调用拷贝构造),且这两个对象都是存放在栈区的。当我们这个测试函数的所有代码执行完毕后,编译器会自动帮我们释放这两个对象,但释放对象时,会调用析构函数,如果我们没有自己写拷贝构造函数,编译器做的是浅拷贝,即 p1 里面所有的属性都只是做一个简单复制到 p2 上,但 m_Height 是一个指针,存储的是栈区某一块内存的地址(假设是 0x0011 这个位置),复制到 p2 上,p2m_Height 指针也指向了相同的地方(0x0011),调用了析构函数的话,实际上对同一块内存做了两次释放的操作(p1 一次、p2 一次),这样的操作是非法的,这也就是浅拷贝带来的问题。


初始化列表:C++提供了初始化列表语法,可以用来初始化属性;

语法:

构造函数的名称() : 属性1(1) , 属性2(2) , ....
{
	....
}

假设有一个人类,属性有三个:int m_A; int m_B; int m_C;

//假设有一个人类,属性有三个:int m_A;  int m_B;  int m_C;

Person() : m_A(10) , m_B(20) , m_C(30)
{
    ....  //这样子写初始化列表,相当于写死了,m_A、m_B、m_C,只能分别等于10、20、30
}

Person(int a,int b,int c) : m_A(a) , m_B(b) , m_C(c)
{
    ....  //这样子写初始化列表更灵活,a、b、c可以后期传入,达到用不同的值初始化m_A、m_B、m_C
}

C++还在学习中O(∩_∩)O,之后还会更新笔记!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值