C++--day06

目录: 
1. C的提高 1-131P 时间七天 
2. C++的基础 132-286P 时间八天 
3. C++的提高 287-378P 时间五天 
4. C/C++的数据结构 379-482P 时间五天 
5. C/C++的设计模式基础 483-540P 时间三天

视频资料:https://www.bilibili.com/video/av27904891?from=search&seid=10891514449061956870


P179  copy构造函数调用时机4-函数返回值是匿名对象

#include<iostream>
using namespace std;


class Location 
{ 
public:
    Location( int xx = 0 , int yy = 0 ) 
    { 
        X = xx ; 
        Y = yy ;  
        cout << "Constructor Object.\n"<<endl ; 
    }
    Location( const Location & p )         //拷贝构造函数 完成对象的初始化
    { 
        X = p.X ; 
        Y = p.Y ;   
        cout << "Copy_constructor called." << endl ;  
    }
    ~Location() 
    { 
        cout << X << "," << Y << " Object destroyed." << endl ; 
    }
    int  GetX () 
    { 
        return X ;
    }        
    int GetY () 
    { 
        return Y ;
    }
private :   
    int  X , Y ;
} ;

//g函数返回一个元素
//结论:函数的返回值是一个元素(复杂类型),返回的是一个新的匿名对象  所以会调用匿名对象类的拷贝对象函数

Location g()
{
    Location A(1,2);
    return A;

}
//
void objplay1()
{
    g();

}
void main()
{
    objplay1();
    system("pause");
}

 输出结果


P180  copy构造函数调用时机3-函数返回值是匿名对象去和留的剖析

#include<iostream>
using namespace std;


class Location 
{ 
public:
    Location( int xx = 0 , int yy = 0 ) 
    { 
        X = xx ; 
        Y = yy ;  
        cout << "Constructor Object.\n"<<endl ; 
    }
    Location( const Location & p )         //拷贝构造函数 完成对象的初始化
    { 
        X = p.X ; 
        Y = p.Y ;   
        cout << "Copy_constructor called." << endl ;  
    }
    ~Location() 
    { 
        cout << X << "," << Y << " Object destroyed." << endl ; 
    }
    int  GetX () 
    { 
        return X ;
    }        
    int GetY () 
    { 
        return Y ;
    }
private :   
    int  X , Y ;
} ;
//g函数返回一个元素
//结论1:函数的返回值是一个元素(复杂类型),返回的是一个新的匿名对象(所以会调用匿名对象类的拷贝对象函数)
//结论2:有关匿名对象的去和留
//       如果用匿名对象初始化另外一个同类型的对象,匿名匿名对象,转成有名对象
//       如果用匿名对象赋值给另外一个同类型的对象,匿名对象被析构

//这么写代码,编译器返回一个新对象(没有名字的匿名对象)
Location g()
{
    Location A(1,2);
    return A;
}
//
void objplay1()
{
    g();

}
void objplay2()
{
    //用匿名对象初始化m,此时C++编译器直接把匿名对象转成m;从匿名转成有名字了
    Location m=g();
    printf("匿名对象被扶正,不会被析构\n");
    cout<<m.GetX()<<endl;

}
void objplay3()
{
    //用匿名对象赋值给m2,匿名对象会析构
    Location m2(1,2);
    m2=g();
    printf("因为用匿名对象=给m2,匿名对象被析构\n");
    cout<<m2.GetX()<<endl;

}
void main()
{
    objplay3();
    system("pause");
}

P181  构造和析构的重点整理

 


P183  构造和析构的总结

1、 构造函数的基础

2、 构造函数的分类

  有参构造函数

  无参构造函数

  赋值构造函数

  默认构造函数

 3、拷贝函数的调用时机

 4、C++编译器提供的构造函数 PK 对象显示初始化方案

 5、拷贝构造函数

 6、构造函数调用规则研究

       若定义构造函数,则必须调用

  若不定义构造函数 使用C++编译提供的构造函数

 7、深拷贝和浅拷贝

 8、构造函数初始化列表

  对象互组

 9、构造和析构的调用顺序研究

  先调用成员变量的构造函数,再调用自己的构造函数

#include <iostream>
using namespace std;

void objplaymain71();

class Test
{
public:
    Test()
    {
        a = 0; 
        b = 0;
        cout << "无参数构造函数  自动被调用" <<endl;
    }
    Test(int _a) //有参数构造函数
    {
        a = _a;
        b = 0;
    }

    Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象
    {
        a = obj.a + 100;
        b = obj.b + 100;
    }
    void printT()
    {
        cout << "a:" << a << "b: "<<b<< endl; 
    }

    ~Test()
    {
        cout<<"我是析构函数 对象生命周期结束时,会被c++编译器自动调用" <<endl;
     }

protected:
private:
    int a;
    int b;
};

// 第3种调用时机
void printTest(Test t)
{
    ;
}

// 1 和 2 
void objplaymain72()
{
    //
    Test t1(1); //ok
    Test t2(t1); 
    Test t3 = t1; //会调用copy构造函数
    printTest(t3);
}


//copy构造函数的第4种调用时机
//返回一个元素  匿名对象
Test getTestObj()
{
    Test t(1);
    return t;
}

void TestNoNameObj()
{
    Test  myt1 =getTestObj(); //用匿名对象初始化 另外一个对象  
    Test  myt2(1);
    myt2 = getTestObj(); //用匿名对象 给 另外一个对象 赋值 匿名对象被析构

}

int main()
{
    //objplaymain();
    objplaymain72();
    TestNoNameObj();
    cout<<"hello..."<<endl;
    system("pause");
    return 0; 
}

void objplaymain71()
{
    Test t1; //ok
    //Test t2() ; //调用无参数构造函数的 错误方法
    //t2.printT();

    //

    Test  t3(1);    //c++编译器自动的调用构造函数
    Test t4 = 4;    //c++编译器自动的调用构造函数
    Test t5 = Test(5); //程序员手工的调用构造函数

    //
    Test t6 = t1;
    return ;
}

P184  构造函数的调用规则研究

默认构造函数

二个特殊的构造函数

1)默认无参构造函数

  当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数

  当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制

构造函数调用规则研究

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数

2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数

3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数

4 )默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。

#include <iostream>
using namespace std;

class Test
{
public:
    
//     Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象
//     {
//         a = obj.a + 100;
//         b = obj.b + 100;
//     }

//     Test(int _a, int _b)
//     {
//         ;
//     }
    Test()
    {
        cout<<"hi"<<endl;
    }
    
    void printT()
    {
        cout << "a:" << a << "b: "<<b<< endl; 
    }

private:
    int a;
    int b;
};

//当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
//当类中定义了有参数构造函数是,c++编译器不会提供无参数构造函数

//在定义类时, 只要你写了构造函数,则必须要用

int main()
{
    Test t1; //调用无参构造函数
    cout<<"hello..."<<endl;
    system("pause");
    return 0;
}

P187  深浅拷贝问题

#define  _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;


class Name
{
public:
    Name (const char *myp)
    {
        m_len=strlen(myp);
        m_p=(char *)malloc(m_len+1);//
        strcpy(m_p,myp);
    }
    //解决方案:手工的编写拷贝构造函数 使用深copy
    Name(const Name& obj1)
    {
        m_len=obj1.m_len;
        m_p=(char *)malloc(m_len+1);
        strcpy(m_p,obj1.m_p);
    }
    ~Name()
    {
        if (m_p!=NULL)
        {
            free(m_p);
            m_p=NULL;
            m_len=0;
        }
    }
private:
    char *m_p;
    int m_len;
};



void objpalymain()
{
    Name obj1("abcde");

    Name obj2=obj1;//默认的拷贝构造函数 C++编译器提供的
}


    
void main()
{
    objpalymain();
    system("pause");
}


 P188  深拷贝和浅拷贝-默认的等号操作符也是浅拷贝

#define  _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;


class Name
{
public:
    Name (const char *myp)
    {
        m_len=strlen(myp);
        m_p=(char *)malloc(m_len+1);//
        strcpy(m_p,myp);
    }
    //解决方案:手工的编写拷贝构造函数 使用深copy
    Name(const Name& obj1)
    {
        m_len=obj1.m_len;
        m_p=(char *)malloc(m_len+1);
        strcpy(m_p,obj1.m_p);
    }
    ~Name()
    {
        if (m_p!=NULL)
        {
            free(m_p);
            m_p=NULL;
            m_len=0;
        }
    }
private:
    char *m_p;
    int m_len;
};



void objpalymain()
{
    Name obj1("abcde");

    //Name obj2=obj1;//默认的拷贝构造函数 C++编译器提供的

    Name obj3("obj3");

    obj3 = obj1;//等号操作
}


    
void main()
{
    objpalymain();
    system("pause");
}

 


P189  构造函数的初始化列表

1、对象初始化列表  

1)对象初始化列表出现原因

  1.必须这样做:

  如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,

  如果没有初始化列表,那么他将无法完成第一步,就会报错。

 

  2.类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值

  当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,

  因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

2)C++中提供初始化列表对成员变量进行初始化

语法规则

    Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)

    {

        // some other assignment operation

    }

3)注意概念

  初始化:被初始化的对象正在创建

  赋值:被赋值的对象已经存在

 

4)注意:

  成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关

  初始化列表先于构造函数的函数体执行

 #include<iostream>

using namespace std;

class A
{
public:
    A(int _a)
    {
        a=_a;
        cout<<"构造函数"<<"a:"<<a<<endl;
    }
    ~A()
    {
        cout<<"A的析构函数"<<"a:"<<a<<endl;
    }
protected:
private:
    int a;
};
//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//  根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//  新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
    B(int _b1,int _b2):a1(1),a2(2),c(0)
    {
        b1=_b1;
        b2=_b2;
    }
    B(int _b1,int _b2,int m,int n):a1(m),a2(n),c(0)
    {
        b1=_b1;
        b2=_b2;
        cout<<"B的构造函数"<<endl;
    }
    ~B()
    {
        cout<<"B的析构函数"<<endl;
    }
protected:
private:
    int b1;
    int b2;
    A a1;
    A a2;
const int c;
};
//2 先执行 被组合对象的构造函数 // 如果组合对象有多个,按照定义顺序, 而不是按照初始化列表的顺序 // 析构函数 : 和构造函数的调用顺序相反 //3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系 //4 初始化列表 用来 给const 属性赋值 void obj10play() { //A a1(10); //1. 参数传递 B objB(1,2,3,4); //2. 调用顺序 } void main() { obj10play(); system("pause"); }

输出结果


P190  强化训练1--构造和析构调用顺序

//对象做函数参数
//1 研究拷贝构造 
//2 研究构造函数,析构函数的调用顺序

//总结 构造和析构的调用顺序

#include "iostream"
using namespace std;

class ABCD 
{
public:
    ABCD(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    ~ABCD()
    {
        printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    int getA() 
    {
        return this->a;
    }
private:
    int a;
    int b;
    int c;
};


class MyE
{
public:
    MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
    {
        cout<<"MyD()"<<endl;
    }
    ~MyE()
    {
        cout<<"~MyD()"<<endl;
    }
    MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
    {
        printf("MyD(const MyD & obj)\n");
    }

protected:
    //private:
public:
    ABCD abcd1; //c++编译器不知道如何构造abc1
    ABCD abcd2;
    const int m;

};

int doThing(MyE mye1)//是一个元素,用实参初始化形参,调用形参拷贝构造函数
{
    printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA()); 
    return 0;
}

int run2()
{
    MyE myE;
    doThing(myE);//实参调用doThing函数
    return 0;
}

//
int run3()
{
    printf("run3 start..\n");

    //ABCD(400, 500, 600); //临时对象的生命周期  

    ABCD abcd = ABCD(100, 200, 300);
    //若直接调用构造函数呢?
    //想调用构造函数对abc对象进行再复制,可以吗?
    //在构造函数里面调用另外一个构造函数,会有什么结果?

    printf("run3 end\n");
    return 0;
}

int main()
{
    run2();
    //run3();
    system("pause");
    return 0;
}

输出结果


P191   强化训练2--匿名对象声明周期

//研究构造函数,析构函数的调用顺序
//总结 构造和析构的调用顺序

#include "iostream"
using namespace std;

class ABCD 
{
public:
    ABCD(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    ~ABCD()
    {
        printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    int getA() 
    {
        return this->a;
    }
private:
    int a;
    int b;
    int c;
};
int run3()
{
    printf("run3 start..\n");

    //ABCD(400, 500, 600); //临时对象的生命周期  

    ABCD abcd = ABCD(100, 200, 300);
    //若直接调用构造函数呢?
    //想调用构造函数对abc对象进行再复制,可以吗?
    //在构造函数里面调用另外一个构造函数,会有什么结果?

    printf("run3 end\n");
    return 0;
}

int main()
{
    run3();
    system("pause");
    return 0;
}

第一种 ABCD(400, 500, 600)输出结果 -- 先调用构造函数然后紧接着调用析构函数:因为是匿名对象,没有人接管它

第二种 ABCD abcd = ABCD(100, 200, 300);输出结果 -- 匿名对象转正abcd,运行完毕之后会调用析构函数

 


 P192  强化训练3--构造中调用构造(产生匿名对象)

#include "iostream"
using namespace std;


//构造中调用构造是危险的行为
class MyTest
{
public:
    MyTest(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    MyTest(int a, int b)
    {
        this->a = a;
        this->b = b;

        MyTest(a, b, 100); //产生新的匿名对象
    }
    ~MyTest()
    {
        printf("MyTest~:%d, %d, %d\n", a, b, c);
    }

protected:
private:
    int a;
    int b;
    int c;

public:
    int getC() const
    {
        return c; 
    }
    void setC(int val) 
    {
        c = val;
    }
};

int main()
{
    MyTest t1(1, 2);
    printf("c:%d\n", t1.getC()); //请问c的值是?
    system("pause");
    return 0;
}

 输出结果:

 

由上节可知,匿名对象的生命周期先调用构造函数然后紧接着调用析构函数:因为是匿名对象,没有人接管它

匿名对象和t1没有关系,调用的是匿名对象的析构函数

 


P193  new和delete的基本语法

1)在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。

   C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。

     注意: new和delete是运算符,不是函数,因此执行效率高。

2)虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。

  new运算符的例子: 

new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)
new int(100);  //开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址
new char[10];  //开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
new int[5][4];  //开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址
float *p=new float (3.14159);  //开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p

  

3)new和delete运算符使用的一般格式为:

 

用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。

4) 应用举例

 

////分配基础类型
void main()
{
    // 
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    free(p);

    int *p2 = new int; //分配基础类型
    *p2 = 20;
    free(p2);
    //
    int *p3 = new int(30);
    printf("*p3:%d \n", *p3);//30delete p3;

    cout<<"hello..."<<endl;
    system("pause");
    return ;
}
//分配数组变量
void main()
{
    //c语言分配数组
    int *p = (int *)malloc(sizeof(int) * 10);  //int array[10];
    p[0] = 1;
    free(p);

    //c++分配数组 
    int *pArray = new int[10] ;
    pArray[1] = 2;
    delete [] pArray; //数组不要把[] 忘记

    char *pArray2 = new char[25] ; //char buf[25]
    delete [] pArray2;


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}
class Test
{
public:
    Test(int _a)
    {
        a = _a;
        cout<<"构造函数执行" <<endl;
    }

    ~Test()
    {
        cout<<"析构函数执行" <<endl;
    }

protected:
private:
    int a;
};

//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数   delete操作符 能执行类的析构函数
void main()
{
    //c 
    Test *pT1 = (Test *)malloc(sizeof(Test));
    free(pT1);

    //c++
    Test *pT2 = new Test(10);
    delete pT2;

    cout<<"hello..."<<endl;
    system("pause");
}

P194   new和delete深入分析

混用测试、异同比较

结论:   malloc不会调用类的构造函数

    Free不会调用类的析构函数

#include <iostream>
using namespace std;

// 1
//        malloc    free                c语言的函数
//        new        delete 操作符    c++的语法

//2        new 基础类型变量  分配数组变量  分配类对象

//3        

////分配基础类型--混搭没有问题
void main01()
{
    // 
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    //free(p);
    delete p;

    int *p2 = new int; //分配基础类型
    *p2 = 20;
    free(p2);
    //
    int *p3 = new int(30);
    printf("*p3:%d \n", *p3);
    //delete p3;
    free(p3);

    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

//分配数组变量
void main02()
{
    //c语言分配数组
    int *p = (int *)malloc(sizeof(int) * 10);  //int array[10];
    p[0] = 1;
    //free(p);
    delete[] p;

    //c++分配数组 
    int *pArray = new int[10] ;
    pArray[1] = 2;
    //delete [] pArray; //数组不要把[] 忘记
    free(pArray);

    char *pArray2 = new char[25] ; //char buf[25]
    delete [] pArray2;


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

class Test
{
public:
    Test(int _a)
    {
        a = _a;
        cout<<"构造函数执行" <<endl;
    }

    ~Test()
    {
        cout<<"析构函数执行" <<endl;
    }

protected:
private:
    int a;
};

//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数   delete操作符 能执行类的析构函数

//    malloc     free   函数      C 只会分配内存大小 不会调用类的构造析构函数
//    new     delete    操作符号  c++的关键字
//结论
void main()
{
    //c 
    Test *pT1 = (Test *)malloc(sizeof(Test));
    //free(pT1);
    delete pT1;

    //c++
    Test *pT2 = new Test(10);
    //delete pT2;
    free(pT2);

    cout<<"hello..."<<endl;
    system("pause");
}

P195  静态成员变量和静态成员函数

1)定义静态成员变量

  • 关键字 static 可以用于说明一个类的成员,

           静态成员提供了一个同类对象的共享机制

  • 把一个类的成员说明为 static 时,这个类无论有多少个对象被创建,这些对象共享这个 static 成员
  • 静态成员局部于类,它不是对象成员
#include<iostream>
using namespace std;

class BB
{
public:
    void printC()
    {
        cout<<"c:"<<c<<endl;
        
    }
    void AddC()
    {
        c=c+1;  //成员函数访问静态数据成员
    }
protected:
private:
    int a;
    int b;
    static int c;//声明与定义静态数据成员
};

int BB::c=10;//声明与定义静态数据成员

void main()
{
    BB b1,b2,b3;
    b1.printC();//10
    b2.AddC();//11
    b3.printC();//11

    system("pause");
}

1)静态成员函数

1)概念

  • 静态成员函数数冠以关键字static
  • 静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
  • 在类外调用静态成员函数用类名 :: ”作限定词,或通过对象调用

2)案例 

猜你喜欢

转载自www.cnblogs.com/yangyuqing/p/10408219.html