没有任何返回值。
初始化列表, 效率更高,用不了this指针
一是使用初始化列表,二是在构造函数体内进行赋值操作。使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。同样看上面的例子,我们使用初始化列表来实现Test2的构造函数

struct Test2
{

Test1 test1 ;
Test2(Test1 &t1):test1(t1){}

}

使用同样的调用代码,输出结果如下。

construct Test1
copy constructor for Test1

第一行输出对应 调用代码的第一行。第二行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表。
举例

lass X {
public:
        int m_i;
        X(int value = 0) :m_i(value)
        {
               printf("this = %p", this);
               cout << "X(int)构造函数被调用" << endl;
        }
        X(const X &tmpv)
        {
               printf("this = %p", this);
               cout << "X拷贝构造函数被调用" << endl;
        }
        X& operator=(const X &tmpv)
        {
               printf("this = %p", this);
               cout << "X拷贝赋值运算符被调用" << endl;
               return *this;
        }
        ~X()
        {
               printf("this = %p", this);
               cout << "X析构函数被调用" << endl;
        }
};
class Y{
public:
        X xobj;//类类型对象
        Y(int tmpvalue) :xobj(1000)//这里构造了xobj,此处若没有使用初始化成员列表,耗费了一次构造函数调用的机会
                           // 编译器角度(没用初始化列表)
                                      //X xobj;
                                      //xobj.X::X();
                                      // 编译器角度(用了初始化列表)
                                      //X xobj;
                                      //xobj.X::X(1000);
        {
              //xobj = 1000;//若不使用初始化列表。这里构造一个临时对象,把临时对象内容给了xobj,之后释放xobj
             //编译器视角
            //x tmpobj;//                          (1)生成临时对象
            //tmpobj.X::X(1000);//                 (2)临时对象调用构造函数
            //xobj.X::operator=(tmpobj);//         (3)调用拷贝赋值运算符
            //tmpobj.X::~X();      //              (4)调用析构函数
        }
};

int n = 1
int m(1)
int k{1}
People p3();//错误, 不构造任何对象,但可以编译通过,但没有这种写法
People p4{};//正常调用构造函数

People p3 = new People()//正常构造
People p4 = new People{};//正常构造

vector<int> array{10, 11}
没有写任何构造函数, 系统会给我们生成一个默认构造函数,析构。
定义了有参构造函数, 编译器就不会提供了默认构造函数了

拷贝构造函数

People(const Peole& p){}
为什么使用引用传递:避免死循环问题。

new 和malloc区别
malloc: 分配内存
free: 释放内存

new:分配内存,调用构造函数
delete:调用析构函数, 释放内存

new实现
void  * operator new(size_t size){
    cout << size << endl;
    void * p = malloc(size);
    return p;
}
//先分配内存,返回地址,编译器再去匹配适当的构造函数

void operator delete(void * p){
 if(p) free(p);
}

比如指定一块内存,
const int size = 1024*1024;
char * buff = new char[size];
People * p2 = new(buff)People(18);//placememt new 系统有的 使用buff内存,而不是开辟内存, 开辟在上面
p2->~People();//首先拆解对象, 假如对象有两个指针成员,分别指向堆中的空间,如果直接free buff内存, 会造成内存泄漏现象
delete buff;
//而不是直接delete p2; 谁分配, 谁释放

= delete 和 = default

People ()= delete;
 People p1; //编译报错;
People ()= default;
People p2; //成功

深拷贝和浅拷贝

默认的拷贝构造函数和operator都是浅拷贝
拷贝构造函数和赋值构造函数区别

#include<iostream>
using namespace std;
 class Test
 {
 public:
     Test (int d=0):data(d)
     {
         cout<<"create Test object:"<<this<<endl;
        
     }
         //Test t1(t)
         //对象对对象一般为拷贝构造,则为对自身的引用,等同于对对象本身进行直接操作
         Test(const Test &t)//有无const不是关键,主要是引用,等同于这块对对主函数对象t进行操作
         {
             cout<<"copy creat Test object:"<<this<<endl;
             data=t.data;
         }
         //~t
         //t2=t1//赋值语句
         Test& operator=(const Test &t)
         {
             cout<<"Assign:"<<this<<"="<<&t<<endl;
             if(this!= &t)
             {
                 data=t.data;
             }
             return *this;
         }
         ~Test()
         {
            cout<<"free Test object:"<<this<<endl;
         }
        public:
                    int GetData()const
                    {
                        return data;
                    }
     private:
         int data;
     };
    
     void main()
     {
         Test t(10);
         
         Test t1(t);//这块等价于 Test t=t1 用t对象初始化t1对象,则用到拷贝构造
         Test t2;
         t2=t1;//此时调动赋值语句 是拿对象给对象赋值
         Test t3=t2//这块是拿对象初始化对象 调动拷贝构造,注意和上一条的区分
     }

直接声明的对象,比如shape a,即在栈上面的对象,所在作用域结束后,会自动执行析构函数,而new出来的在堆上的对象,不调用delete,即使它所在的作用域已经结束,也不会调用析构函数,根据另外一篇文章内所述,可能程序结束后,OS(操作系统)会回收其堆内占用的内存,这样就不知道会干什么,会不会调用析构了。


Connie
4 声望0 粉丝

« 上一篇
类和对象2
下一篇 »
封装总结