C++基础讲解第三期(超详细)每天更新哈,大家一起加油

C++基础讲解第三期 代码中也有对应知识注释,别忘看,一起学习!

  • 一、面向编程练习(根据前两篇笔记,先来个题目练练手吧)
  • 二、对象的构造和析构
    • 1、构造函数
    • 2.构造函数的重载和调用
    • 3、拷贝构造函数
    • 3、深拷贝
    • 4、默认构造函数
    • 5、析构函数
    • 6、匿名对象
    • 7、对象的动态创建和释放(malloc、free、new、delete)
      • 1) c++中 malloc使用
      • 2)malloc和new的区别?
    • 8、构造函数的参数初始化列表

一、面向编程练习(根据前两篇笔记,先来个题目练练手吧)

要求:设计一个圆形类和一个点类,计算点在圆外还是圆内还是圆上,求点和圆的关系


  1. 属性:横坐标和纵坐标
    方法:点和点之间的距离

  2. 属性:圆心,半径
    方法:设置圆心和半径,判断点和圆的关系
//circle.cpp
#include"circle.h"
#include

void Dit::set(int x, int y)
{
    m_x = x;
    m_y = y;
}

int Dit::getx()
{
    return m_x;
}

int Dit::gety()
{
    return m_y;
}

double Dit::Distance(Dit d)
{
    double s;
    s = sqrt((d.m_x - m_x)*(d.m_x - m_x) + (d.m_y-m_y)*(d.m_y-m_y));
    cout << "d.m_x = " << d.m_x << "d.m_y = " << d.m_y<<"m_x= "<< m_x<<"m_y= "<< m_y <<endl;
    cout << "两点之间的距离是:" << s << endl;
    return s;
}

void Circle::set(int x, int y, int r)
{
    m_d.set(x ,y);
    m_r = r;
}

void Circle::Relationship(Dit d)
{
    double distance = d.Distance(m_d);
    if(distance == m_r)
    {
        cout<< "点在圆上" << endl;
    }
    else if(distance > m_r)
    {
        cout << "点在圆外" << endl;
    }
    else
    {
        cout << "点在园内" << endl;
    }
}
//circle.h
#ifndef _H_CIRCLE_
#define _H_CIRCLE_

#include
using namespace std;

class Dit
{
    private:
        int m_x;
        int m_y;
    public:
        double Distance(Dit d);
        void set(int x, int y);
        int getx();
        int gety();
};

class Circle
{
    private:
        Dit m_d;
        int m_r;

    public:
        void set(int x, int y, int r);
        void Relationship(Dit d);
};

#endif
#include"circle.h"

int main()
{
    int d_x = 1;
    int d_y = 1;

    Dit d;
    Circle c;
    d.set(1, 1);
    c.set(3, 4, 5);

    c.Relationship(d);
    return 0;
}

二、对象的构造和析构

1、构造函数

在C++中,有一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户显示调用(也不能调用),而且在创建对象的时候自动执行。这种特殊的成员函数称为构造函数

几点说明:
1.构造函数的函数名和类名必须相同
2.构造函数不能有返回值,函数体内不能有return语句
3.构造函数在定义对象时会自动调用,不需要手动调用

#include
using namespace std;

class Array
{
    private:
        int *data;  //数组的起始地址
        int size;   //数组的大小

    public:
        Array();    //无参构造函数,函数名和类名相同,没有返回值,完成对象的初始化操作
        void SetVal(int index,int val);
        int GetVal(int index);
        ~Array(); //析构函数,函数名是:类名+~,没有返回值
};

Array::Array()
{
    cout<<"array的无参构造函数"<<endl;
    size = 5;
    data = (int *)malloc(sizeof(int)*size);
}   

void Array::SetVal(int index,int val)
{
    data[index] = val;
}

int Array::GetVal(int index)
{
    return data[index];
}

Array::~Array() //析构函数,函数名是:类名+~,没有返回值
{
    cout<<"Array的析构函数"<<endl;
    if(NULL != data)
    {
        free(data);
        data = NULL;
    }
}

int main()
{
    Array a1; //创建对象时自动调用构造函数
    for(int i = 0; i < 5; i++)
    {
        a1.SetVal(i,i+1);
    }

    for(int i = 0;i<5;i++)
    {
        cout << a1.GetVal(i) << "" <<endl;
    }

    return 0;
}

输出为:
array的无参构造函数
1 2 3 4 5
Array的析构函数

2.构造函数的重载和调用

和普通函数一样,构造函数时允许重载的,一个类可以有多个重载的构造函数,在创建对象时根据传递的实参去决定调用哪个构造函数。

#include
using namespace std;

class Array
{
    private:
        int *data;  //数组的起始地址
        int size;   //数组的大小

    public:
        Array();    //无参构造函数,函数名和类名相同,没有返回值,完成对象的初始化操作
        Array(int s); //有参构造函数
        Array(int s, int z); //有两个参数的构造函数
        void SetVal(int index,int val);
        int GetVal(int index);
        ~Array(); //析构函数,函数名是:类名+~,没有返回值
};

Array::Array()
{
    cout<<"array的无参构造函数"<<endl;
    size = 5;
    data = (int *)malloc(sizeof(int)*size);
}   

Array::Array(int s)
{
    size = s;
    data = (int *)malloc(sizeof(int)*size);
    cout <<"Array的有参构造函数, size = " << size <<endl;
}

Array::Array(int s, int z)
{
    size = s;
    data = (int *)malloc(sizeof(int)*size);
    cout <<"Array的两个参数的构造函数, size= "<< size << endl;
}

void Array::SetVal(int index,int val)
{
    data[index] = val;
}

int Array::GetVal(int index)
{
    return data[index];
}

Array::~Array() //析构函数,函数名是:类名+~,没有返回值
{
    cout<<"Array的析构函数"<<endl;
    if(NULL != data)
    {
        free(data);
        data = NULL;
    }
}

int main()
{
    /*
    Array a1; //创建对象时自动调用构造函数
    for(int i = 0; i < 5; i++)
    {
        a1.SetVal(i,i+1);
    }

    for(int i = 0;i<5;i++)
    {
        cout << a1.GetVal(i) << "" <
    Array a2(6);//初始化方式为括号法
    Array a3(7,8);

    Array a4 = 100;//初始化方法为等号法,有局限,只能有一个参数
    return 0;
}
运行结果:
Array的有参构造函数, size = 6
Array的两个参数的构造函数, size= 7
Array的有参构造函数, size = 100
Array的析构函数
Array的析构函数
Array的析构函数

3、拷贝构造函数

1.概念
格式:className(const className &obj)
拷贝构造函数也称为赋值拷贝构造函数
作用:用一个已有的对象去初始化另一个对象

Array::Array(const Array &a)
{
    cout<<"Array的拷贝构造函数"<<endl;
}

2.拷贝构造函数调用的时机
1)用一个对象初始化另一个对象
Array a5 = a4;
Array a6(a4);
Array a7 = Array(a4);
2)当函数的形参是一个对象时,比如:

void print(Array a)
{
    a.GetVal(1);
}
Array a1;
print(a1);

输出结果为:
Array的有参构造函数, size = 100
Array的拷贝构造函数
Array的析构函数
Array的析构函数
注意:这里,我在有参的构造函数中,给data指针分配了内存,在析构中free了内存,所以当我们在拷贝构造函数中没有给指针分配内存时,那么这个指针的内存会被释放两次,那么就会发生crash,这一点后面会讲到。这里没有crash是因为我将析构函数中的free函数注释了

3)函数返回值是一个对象

Array& Func()
{
    Array a1;
    return a1;
}

Array a2 = Func();

首先会调用无参构造函数,因为Func中创建了a1对象,将a1 return时,会产生匿名对象(没有名字的对象),因为这时候a1被释放了(因为a1是局部的),那么这时候用这个a1匿名对象初始化a2,就会调用匿名对象的拷贝构造函数,编译器实际上不会给a2分配空间,而是将匿名对象给了a2,直接用a2给这个匿名对象命名,所以不会产生两次拷贝构造。
C++基础讲解第三期(超详细)每天更新哈,大家一起加油_第1张图片

3、深拷贝

#include
using namespace std;

class Array
{
    private:
        int *data;
        int size;

    public:
        Array(int s)
        {
            cout <<"array的有参构造函数"<<endl;
            size = s;
            data = (int *)malloc(sizeof(int)*size);
        }

        ~Array()
        {
            cout <<"array的析构函数"<<endl;
            if(data != NULL)
            {
                free(data);
                data = NULL;
            }
        }

        Array(Array &a)//深拷贝构造函数
        {
            cout <<"array的深拷贝构造函数"<< endl;
            size = a.size;
            data = (int*)malloc(sizeof(int)*size);//和浅拷贝的区别在于,重新给指针分配了空间,用于保存数组中的值。不会和被拷贝的数组同用一片空间

            for(int i = 0; i < size; i++)
            {
                data[i] = a.data[i];
            }
        }
};

int main()
{
    Array a1(10);
    Array a2(a1);

    return 0;
    
输出:
array的有参构造函数
array的深拷贝构造函数
array的析构函数
array的析构函数
}

C++基础讲解第三期(超详细)每天更新哈,大家一起加油_第2张图片

4、默认构造函数

#include
using namespace std;

class Test
{
    public:
    Test()
    {
        cout <<"Test 的无参构造函数"<<endl;
    }

    Test(int a)
    {
        cout <<"Test 的一个参数构造函数"<<endl;
    }

    Test(const Test &a)
    {
        cout <<"Test 的拷贝构造函数"<<endl;
    }
};

int main()
{
    Test t1;//编译器会默认提供无参构造函数
    Test t2(t1);//编译器会提供默认的拷贝构造函数(浅拷贝)
    Test t3;//一旦提供了无参构造函数,编译器就不再提供无参构造函数
    Test t4;//一旦提供了有参的构造函数,编译器不会再提供默认的无参构造函数
    Test t5;//一旦提供了拷贝构造函数,编译器也不会再提供默认的无参构造函数
    return 0;
}

5、析构函数

1). 格式: ~Array()析构函数
2). 概念:
当一个对象销毁时,系统会自动调用一个函数来进行清理工作,如释放内存,关闭打开的文件,这个函数就是析构函数。
析构函数没有参数,不能被重载,一个类中只能有一个析构函数,如果用户没有提供析构函数,编译器会自动生成一个默认的析构函数。

6、匿名对象

#include
using namespace std;

class Test
{
    public:
    Test()
    {
        cout <<"Test 的无参构造函数"<<endl;
    }

    Test(int a)
    {
        cout <<"Test 的一个参数构造函数"<<endl;
    }

    Test(const Test &a)
    {
        cout <<"Test 的拷贝构造函数"<<endl;
    }

    ~Test()
    {
        cout<<"test的析构函数"<<endl;
    }
};

int main()
{
    Test(); //匿名对象,本行代码执行完毕,立即被释放,也就是立即调用析构函数
    cout<<"*************"<<endl;
    return 0;
}

7、对象的动态创建和释放(malloc、free、new、delete)

1) c++中 malloc使用

#include
using namespace std;

class Test
{
    public:
    Test()
    {
        cout <<"Test 的无参构造函数"<<endl;
    }

    Test(int a)
    {
        cout <<"Test 的一个参数构造函数"<<endl;
    }

    Test(const Test &a)
    {
        cout <<"Test 的拷贝构造函数"<<endl;
    }

    ~Test()
    {
        cout<<"test的析构函数"<<endl;
    }
};

int main()
{
    //Test t1; 栈空间创建对象
    //c++中不用malloc,是因为其不会自动调用构造函数和析构函数
    Test *pt = (Test *)malloc(sizeof(Test));//在堆空间申请一个对象大小的内存
    if(NULL == pt)
    {
        cout << "malloc is failed" <<endl;
    }

    free(pt);
    pt = NULL;


    Test *pt2 = new Test; //在堆中申请内存,并且自动调用构造函数

    delete pt2; //自动调用析构函数
    pt2 = NULL;
	
	int *p2 = new int;//给整形分配空间
	delete p3;
	p3 = NULL;

	char *p3 = new char;//给一个字符申请空间
	delete p3;
	p3 = NULL;

	char *p4 = new char[10]; //给10个字符申请空间
	detele []p4;
	p4 = NULL;

	Test *t2 = new Test(1);//给一个Test对象申请空间,并且初始化
	delete t2;
	t2 = NULL;

	Test *t3 = new Test[5]{1,2,3,4,5};//申请十个Test类对象,并初始化
    return 0;
}

2)malloc和new的区别?

1.malloc是函数,new是关键字
2.malloc的返回值类型是void*, new是申请空间的类型
3.malloc不能初始化空间,new可以初始化
4.malloc不能自动调用构造函数,new可以

8、构造函数的参数初始化列表

#include
using namespace std;

class Date
{
    private:
        int m_year;
        int m_month;
        int m_day;

    public:
       /* Date()
        {
            cout<<"data的无参构造函数"<
        Date(int y, int m, int d)
        {
            cout << "data的有参构造函数"<< endl;
            m_year = y;
            m_month = m;
            m_day = d;
        }

        ~Date()
        {
            cout <<"data的析构函数"<< endl;
        }
};

calss Test
{
	private:
		Test(int s)
		{
			cout << "Test的构造函数"<< endl;
		}
}
//对象的初始化列表:1.当类对象作为成员变量,并且该类没有提供无参构造函数时,我们需要使用初始化列表 2.成员变量用const修饰(初始化和赋值不同)也只能使用初始化列表
class Student
{
    private:
        const int id;
        Date birth;
		Tets t1;
    public:
        Student(int i, int y, int m, int d):t1(10),birth(y,m,d),id(i)
        {
            cout<<"student 有参构造函数"<<endl;
        }
};

int main()
{
    Student s1(100,1996,2,28);

    return 0;
}

几点说明:
1、初始化列表优先于当前对象的构造函数先执行
2、子对象(就是上面代码中的Data成员对象)的初始化顺序和其在初始化列表的排序无关,但和在类中声明的顺序有关,先声明的先初始化。所以先初始化Data,然后是Test,最后是Student。
3、析构函数的调用顺序,和构造函数正好相反
4、参数的初始化列表有一个很重要的作用,那就是初始化const的成员变量

你可能感兴趣的:(c++基础讲解,c++,算法,开发语言)