【CPP】拷贝赋值

1- Default Constructors

  • Default constructors: a constructor which can be called without arguments
  • If you define no constructors, the compiler automatically provide one MyTime::MyTime(){}
    If you define constructors, the compiler will not generate a default one.
class MyTime
{
  public:
    MyTime (int n) {...}
};

MyTime mt; // no appropriate constructor

编译报错,没有合适的构造函数

  • To avoid ambiguous
class MyTime
{
  public:  // two default constructors
    MyTime () {...}
    MyTime (int n = 0) {...}
};

MyTime mt; // which constructor?

编译报错,不知道调用哪个

Implicitly-Defined Destructor

  • If no destructor is defined, the compiler will generate an empty one
MyTime::~MyTime(){}

-Memory allocated in constructors is normally released in a destructor

Default Copy Constructors

  • A copy constructor. Only one parameter, or the rest have default values.
MyTime::MyTime(MyTime & t){...}
MyTime t1(1, 59);
MyTime t2(t1);  // copy constructor
MyTime t3 = t1;  // copy constructor
  • Default copy constructor

(1) If no user-defined copy constructors, the compiler will generate one
(2) Copy all non-static data members

Default Copy Assignment

  • Assignment operators: =, +=, -=
  • Copy assignment operator
MyTime & MyTime::operator=(MyTime &){...}
MyTime t1(1, 59);
MyTime t2 = t1;  // copy constructor
t2 = t1;  // copy assignment
  • Default copy assignment operator

(1) If no user-defined copy assignment constructors, the compiler will generate one
(2) Copy all non-static data member

2- An Example with Dynamic Memory

A Simple String Class

Example1

mystring.hpp

#pragma once

#include 
#include 

class MyString
{
  private:
    int buf_len;
    char * characters;
  public:
    MyString(int buf_len = 64, const char * data = NULL)
    {
        std::cout << "Constructor(int, char*)" << std::endl;
        this->buf_len = 0;
        this->characters = NULL;
        create(buf_len, data);
    }
    ~MyString()
    {
        delete []this->characters;
    }
    bool create(int buf_len,  const char * data)
    {
        this->buf_len = buf_len;

        if( this->buf_len != 0)
        {
            this->characters = new char[this->buf_len]{};
            if(data)
                strncpy(this->characters, data, this->buf_len);
        }
    
        return true;
    }
    friend std::ostream & operator<<(std::ostream & os, const MyString & ms)
    {
        os << "buf_len = " << ms.buf_len;
        os << ", characters = " << static_cast<void*>(ms.characters);
        os << " [" << ms.characters << "]";
        return os;
    }
};

main.cpp

#include 
#include "mystring.hpp"

using namespace std;

// Why memory leak and memory double free?
int main()
{
    MyString str1(10, "Shenzhen");
    cout << "str1: " << str1 << endl;

    MyString str2 = str1; 
    cout << "str2: " << str2 << endl;

    MyString str3;
    cout << "str3: " << str3 << endl;
    str3 = str1;
    cout << "str3: " << str3 << endl;

    return 0;
}
Constructor(int, char*)
str1: buf_len = 10, characters = 0x11e605ef0 [Shenzhen]
str2: buf_len = 10, characters = 0x11e605ef0 [Shenzhen]
Constructor(int, char*)
str3: buf_len = 64, characters = 0x11e605e10 []
str3: buf_len = 10, characters = 0x11e605ef0 [Shenzhen]
a.out(64132,0x1dd21d300) malloc: Double free of object 0x11e605ef0
a.out(64132,0x1dd21d300) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out

报错。

Dynamic Memory in Objects

  • MyString str1(10, "Shenzhen");

调用了自己定义的构造函数
【CPP】拷贝赋值_第1张图片

  • MyString str2 = str1;

调用了拷贝构造函数,默认的拷贝

【CPP】拷贝赋值_第2张图片

  • MyString str3;

调用了默认的构造函数,就是我们定义的构造函数

【CPP】拷贝赋值_第3张图片

  • str3 = str1;

Default Copy Assignment

【CPP】拷贝赋值_第4张图片

三个对象指向同一个内存

在这里,当作用域结束,首先释放str3,直接把character指向的内存释放了,导致后面释放str2时找不到需要释放的内存,故报错

另外,还有一块内存地址已经没人知道,造成内存泄漏

3-Solution 1: Hard Copy

上一个问题是多个对象指向同一块内存,现在的解决办法是每个对象都要保证拥有自己独立的内存

Copy Constructor

  • Provide a user-defined copy constructor
    MyString(const MyString & ms)
    {
        std::cout << "Constructor(MyString&)" << std::endl;
        this->buf_len = 0;
        this->characters = NULL;
        create(ms.buf_len, ms.characters);
    }
  • create() release the current memory and allocate a new one
  • this->characters will not point to ms.characters
  • It’s a HARD COPY

Copy Assignment

  • Provide a user-defined copy assignment
    MyString & operator=(const MyString &ms)
    {
        create(ms.buf_len, ms.characters);
        return *this;
    }

Example2

mystring.hpp

#pragma once
#include 
#include 

class MyString
{
  private:
    int buf_len;
    char * characters;
  public:
    MyString(int buf_len = 64, const char * data = NULL)
    {
        std::cout << "Constructor(int, char*)" << std::endl;
        this->buf_len = 0;
        this->characters = NULL;
        create(buf_len, data);
    }
    MyString(const MyString & ms)
    {
        std::cout << "Constructor(MyString&)" << std::endl;
        this->buf_len = 0;
        this->characters = NULL;
        create(ms.buf_len, ms.characters);
    }
    ~MyString()
    {
        release();
    }
    MyString & operator=(const MyString &ms)
    {
        std::cout << "Copy Assignment " << std::endl;
        create(ms.buf_len, ms.characters);
        return *this;
    }
    bool create(int buf_len,  const char * data)
    {
        release();

        this->buf_len = buf_len;

        if( this->buf_len != 0)
        {
            this->characters = new char[this->buf_len]{};
        }
        if(data)
            strncpy(this->characters, data, this->buf_len);

        return true;
    }
    bool release()
    {
        this->buf_len = 0;
        if(this->characters!=NULL)
        {
            delete []this->characters;
            this->characters = NULL;
        }
        return 0;
    }
    friend std::ostream & operator<<(std::ostream & os, const MyString & ms)
    {
        os << "buf_len = " << ms.buf_len;
        os << ", characters = " << static_cast<void*>(ms.characters);
        os << " [" << ms.characters << "]";
        return os;
    }
};

main.cpp

#include 
#include "mystring.hpp"

using namespace std;

// Why memory leak and memory double free?
int main()
{
    MyString str1(10, "Shenzhen");
    cout << "str1: " << str1 << endl;

    MyString str2 = str1; 
    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;

    MyString str3;
    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;
    cout << "str3: " << str3 << endl;
    str3 = str1;
    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;
    cout << "str3: " << str3 << endl;

    return 0;
}
Constructor(int, char*)
str1: buf_len = 10, characters = 0x131605ef0 [Shenzhen]
Constructor(MyString&)
str1: buf_len = 10, characters = 0x131605ef0 [Shenzhen]
str2: buf_len = 10, characters = 0x131606040 [Shenzhen]
Constructor(int, char*)
str1: buf_len = 10, characters = 0x131605ef0 [Shenzhen]
str2: buf_len = 10, characters = 0x131606040 [Shenzhen]
str3: buf_len = 64, characters = 0x131605e10 []
Copy Assignment 
str1: buf_len = 10, characters = 0x131605ef0 [Shenzhen]
str2: buf_len = 10, characters = 0x131606040 [Shenzhen]
str3: buf_len = 10, characters = 0x131605e70 [Shenzhen]
  • MyString str1(10, "Shenzhen");

调用自己定义的构造函数
【CPP】拷贝赋值_第5张图片

  • MyString str2 = str1;

调用自己定义的拷贝构造函数

【CPP】拷贝赋值_第6张图片

  • MyString str3;

调用自己定义的构造函数

【CPP】拷贝赋值_第7张图片

  • str3 = str1;

调用自己定义的拷贝赋值函数,先将被赋值的对象的内存销毁,再申请新的地址,并将赋值对象的内容赋值过来
【CPP】拷贝赋值_第8张图片

4-Solution 2: Soft Copy

Problem of Hard Copy

  • Frequently allocate and free memory
  • Time consuming when the memory is big
    内存利用率较低

BUt…

  • If several objects share the same memory, who should release it?

Cv Mat struct

【CPP】拷贝赋值_第9张图片

  • union 联合体,共享同一块地址

【CPP】拷贝赋值_第10张图片

  • unchar* data; : 数据指针
  • UMatData* u; : 用来计算这块内存没引用了多少次
  • 两个共享一块内存,一起释放,一起申请

Solution in OpenCV

  • The allocated memory can be used by multiple object
  • Mat::u ->refcount is used to count the times the memory is referenced
  • CV_XADD: macro for atomic add

【CPP】拷贝赋值_第11张图片

  • CV_XADD(&m.u->refcount, 1); : 宏对refcount加1,用宏而不直接加1,是考虑到并行计算的数据冲突问题
  • data = m.data; : 将被赋值对象的指向赋值对象的同一个内存地址。即还是以下这种情况

【CPP】拷贝赋值_第12张图片

只不过因为有了指针引用次数,内存释放会等到refcount 的值为0时才进行真正的内存释放

  • Copy constructor of cv::Mat

【CPP】拷贝赋值_第13张图片

  • Release 函数

【CPP】拷贝赋值_第14张图片

  • if (u && CV_XADD(&u->refcount, -1) == 1): 宏做减1,返回加法操作前的那个值,也就是当减1为0,加法操作前为1的情况,这个对象是对这个数据的唯一的引用,没有其他的对象引用了,所以可以直接可以申请释放内存

5-Smart Pointers

C++可不可以只管申请不管释放?

可以。

std::shared_ptr

  • Smart pointers are used to make sure that an object can be deleted when it is no longer used
  • Several shared pointers can share/point to the same object
  • The object is destroyed when no shared_ptr points to it
std::shared_ptr<MyTime> mt1(new MyTime(10));
std::shared_ptr<MyTime> mt2 = mt1;

auto mt1 = std::make_shared<MyTime>(1, 70);
  • mt2 = mt1 : mt1 和 mt2指向同一个对象

shared_ptr.cpp

#include 
#include 

class MyTime
{
    int hours;
    int minutes;
  public:
    MyTime(): hours(0), minutes(0)
    {
        std::cout << "Constructor MyTime()" << std::endl;
    }
    MyTime(int m): hours(0), minutes(m)
    {
        std::cout << "Constructor MyTime(int)" << std::endl;
        this->hours +=  this->minutes / 60;
        this->minutes %= 60;
    }
    MyTime(int h, int m): hours(h), minutes(m)
    {
        std::cout << "Constructor MyTime(int,int)" << std::endl;
        this->hours +=  this->minutes / 60;
        this->minutes %= 60;
    }
    ~MyTime()
    {
        std::cout << "Destructor MyTime(). Bye!" << std::endl;
    }
    MyTime operator+(int m) const
    {
        MyTime sum;
        sum.minutes = this->minutes + m;
        sum.hours = this->hours;
        sum.hours +=  sum.minutes / 60;
        sum.minutes %= 60;
        return sum;
    }
    friend std::ostream & operator<<(std::ostream & os, const MyTime & t)
    {
        std::string str = std::to_string(t.hours) + " hours and " 
                        + std::to_string(t.minutes) + " minutes.";
        os << str;
        return os;
    }
};


int main()
{
    // std::shared_ptr mt0 = new MyTime(0,70); //error
    // MyTime * mt1 = std::make_shared(1, 70); //error
    // {
    //     std::shared_ptr mt1(new MyTime(10));
    //     std::cout << *mt1 << std::endl;
    // }

    {
        std::shared_ptr<MyTime> mt1 = std::make_shared<MyTime>(1, 70);
        std::shared_ptr<MyTime> mt2 = mt1;
        std::shared_ptr<MyTime> mt3 = mt2;

        std::cout << "mt1: " << *mt1 << std::endl;
        std::cout << "mt2: " << *mt2 << std::endl;
        std::cout << "mt3: " << *mt3 << std::endl;
        std::cout << "use_count() = " << mt2.use_count() << std::endl;

        {
            auto mt4 = mt3;
            *mt4 = *mt4 + 50;
            std::cout << "use_count() = " << mt3.use_count() << std::endl;
        }
        std::cout << "mt3: " << *mt3 << std::endl;
        std::cout << "use_count() = " << mt3.use_count() << std::endl;
    }

    return 0;
}

Constructor MyTime(int,int)
mt1: 2 hours and 10 minutes.
mt2: 2 hours and 10 minutes.
mt3: 2 hours and 10 minutes.
use_count() = 3
Constructor MyTime()
Destructor MyTime(). Bye!
use_count() = 4
mt3: 3 hours and 0 minutes.
use_count() = 3
Destructor MyTime(). Bye!
  • std::shared_ptr mt1 = std::make_shared(1, 70);: 触发了构造函数,并使用共享指针
  • std::shared_ptr mt2 = mt1;: 拷贝赋值并没有触发构造函数,说明只有一个对象
  • use_count(): 记录有多个指针引用对象
  • auto mt4 = mt3;: 这里只是拷贝,并没有触发构造函数
  • *mt4 = *mt4 + 50;:加法运算符有一个临时变量创建,所以触发了构造函数,加法结束后作用域结束,临时变量销毁,触发析构函数
  • 从头到尾,主函数只有一个对象地址

std::unique_ptr

  • Different from std::shared_ptr, a std::unique_ptr will point to an object, and not allow others to point to
  • but an object pointed by a std::unique_ptr can be moved to another pointer
std::unique_ptr<MyTime> mt1(new MyTime(10));
std::unique_ptr<MyTime> mt2 = std::make_unique<MyTime>(80);  // C++17

std::unique_ptr<MyTime> mt3 = std::move(mt1);

unique_ptr.cpp

#include 
#include 

class MyTime
{
    int hours;
    int minutes;
  public:
    MyTime(): hours(0), minutes(0)
    {
        std::cout << "Constructor MyTime()" << std::endl;
    }
    MyTime(int m): hours(0), minutes(m)
    {
        std::cout << "Constructor MyTime(int)" << std::endl;
        this->hours +=  this->minutes / 60;
        this->minutes %= 60;
    }
    MyTime(int h, int m): hours(h), minutes(m)
    {
        std::cout << "Constructor MyTime(int,int)" << std::endl;
        this->hours +=  this->minutes / 60;
        this->minutes %= 60;
    }
    ~MyTime()
    {
        std::cout << "Destructor MyTime(). Bye!" << std::endl;
    }
    MyTime operator+(int m) const
    {
        MyTime sum;
        sum.minutes = this->minutes + m;
        sum.hours = this->hours;
        sum.hours +=  sum.minutes / 60;
        sum.minutes %= 60;
        return sum;
    }
    friend std::ostream & operator<<(std::ostream & os, const MyTime & t)
    {
        std::string str = std::to_string(t.hours) + " hours and " 
                        + std::to_string(t.minutes) + " minutes.";
        os << str;
        return os;
    }
};


int main()
{
    std::unique_ptr<MyTime> mt1(new MyTime(10));
    std::unique_ptr<MyTime> mt2 = std::make_unique<MyTime>(80); //c++17

    std::cout << "mt1: " <<*mt1 << std::endl;
    std::cout << "mt2: " <<*mt2 << std::endl;

    // std::unique_ptr mt3 = mt1; // error
    std::unique_ptr<MyTime> mt3 = std::move(mt1);
    // std::shared_ptr mt3 = std::move(mt1);//okay
    
    // std::cout << "mt1: " <<*mt1 << std::endl;
    std::cout << "mt3: " <<*mt3 << std::endl;

    return 0;
}

g++ unique_ptr.cpp --std=c++17
./a.out
Constructor MyTime(int)
Constructor MyTime(int)
mt1: 0 hours and 10 minutes.
mt2: 1 hours and 20 minutes.
mt3: 0 hours and 10 minutes.
Destructor MyTime(). Bye!
Destructor MyTime(). Bye!

How to Understand Smart Pointers

  • Let’s look at their definitions
template<class T> class shared_ptr;

template<
  class T,
  class Deleter = std::default_delete<T>
>class unique_ptr;
  • mt1 and mt2 are two objects of type shared_ptr<>
  • You can do a lot in the constructors and the destructor
std::shared_ptr<MyTime> mt1(new MyTime(10));
std::shared_ptr,MyTime> mt2 = mt1;

Lab

Does shared pointer always releases memory?

#include 
#include 

using namespace std;

class B;

class A
{
    public:
    shared_ptr<B> pb;
    A() { cout << "Constructor A " << endl;}
    ~A() {cout << "Destructor A " << endl; }


};


class B{
    public:
    shared_ptr<A> pa;
    B() { cout << "Constructor B "<< endl;}
    ~B() {cout << "Destructor B " << endl;}
};


int main()
{
    shared_ptr<A> spa = make_shared<A> ();
    shared_ptr<B> spb = make_shared<B> ();

    spa->pb = spb;
    spb->pa = spa;

    return 0;
}


Constructor A 
Constructor B 

There is no Destructor invoked, no memory released.

Could the program be compiled successfully?

  • Why?
  • Modify the program until it passes the compilation. Then run the program.
  • What will happen?
  • Explain the result
#include 
#include 
using namespace std;
int main()
{
    double *p_reg = new double(5); 
    shared_ptr<double> pd;
    pd = p_reg;
    pd = shared_ptr<double>(p_reg); 
    cout<<"*pd="<<*pd<<endl;
    shared_ptr<double> pshared = p_reg; 
    shared_ptr<double> pshared(p_reg);       
    cout << "*pshred = " << *pshared << endl;
    string str(“Hello World!"); 
    shared_ptr<string> pstr(&str); 
    cout << "*pstr = " << *pstr << endl;
    return 0; 
}

Modified:

#include  
#include 
using namespace std;


int main()
{
    shared_ptr<double> p_reg(new double(5)); 
    shared_ptr<double> pd;
    pd = p_reg;
    cout<<"*pd="<<*pd<<endl;

    shared_ptr<double> pshared = p_reg; 
    cout << "*pshred = " << *pshared << endl;


    shared_ptr<string> pstr(new string ("Hello World!")); 
    cout << "*pstr = " << *pstr << endl;
    
    
    return 0; 
}

Create a class Matrix to describe a matrix.

  • The element type is float.
  • One member of the class is shared_ptr for the matrix data.
  • The two matrices can share the same data through a copy constructor or a copy assignment.
  • The following code can run smoothly without memory problems.
class Matrix{...};
Matrix a(3,4);
Matrix b(3,4);
Matrix c = a + b;
Matrix d = a;
d = b;

Solution

matrix.hpp

#pragma once
#include 
#include 


class Matrix
{
  private:
    size_t rows;
    size_t cols;
    
  public:
    std::shared_ptr<float> data;
    Matrix(size_t r, size_t c)
    {
        std::cout << "Constructor Matrix(r, c) " << std::endl;
        if ( r * c == 0)
        {
            rows = 0;
            cols = 0;
            data = nullptr;
        }
        else{
            rows = r;
            cols = c;
            std::shared_ptr<float> temp(new float[r * c]);
            data = temp;
        }
    }

    Matrix(const Matrix & m): rows(m.rows), cols(m.cols), data(m.data){ 
        std::cout << "Constructor Matrix(M) " << std::endl;
    }

    ~Matrix() {std::cout << "Destructor Matrix " << std::endl; }


    friend std::ostream & operator<<(std::ostream & os, const Matrix & m)
    {
        os << "size (" << m.rows << "x" << m.cols << ")" << std::endl;
        os << "[" << std::endl;
        for (size_t r = 0; r < m.rows; r++)
        {
            for(size_t c = 0; c < m.cols; c++)
                os << m.data.get()[r * m.cols + c] << ", ";
            os << std::endl;
        }
        os << "]";
        return os;
    }

    Matrix operator+(const Matrix &m)
    {
        std::cout << "Matrix Add " << std::endl;
        if (this->cols != m.cols || this->rows != m.rows)
        {
            std::cout << "The two matrices dimension must be the same. " << std::endl;
        }
        Matrix sum(m.rows, m.cols);
        size_t ind = 0;
        for (size_t r = 0; r < m.rows; r++)
        {
            for (size_t c = 0; c < m.cols; c++)
            {
                ind = r * m.cols + c;
                sum.data.get()[ind] = this->data.get()[ind] + m.data.get()[ind];
            }
        }

        return sum;

    }



    void setElement (int a, int b, float x)
    {   
        this->data.get()[a * this->cols + b] = x;
    }

    float getElement (int a, int b)
    {
        return this->data.get()[a * this->cols + b];
    }

};

matrix.cpp

#include 
#include 
#include "matrix.hpp"


int main()
{
    Matrix m1(3,8);
    Matrix m2(4,8);
    std::cout << "&m1 = " << &m1 << std::endl;
    std::cout << "&m2 = " << &m2 << std::endl;
    std::cout << "m1.data = " << m1.data << std::endl;
    std::cout << "m2.data = " << m2.data << std::endl;
    std::cout << "m1.data.get() = " << m1.data.get() << std::endl;
    std::cout << "m2.data.get() = " << m2.data.get() << std::endl;

    m2 = m1;
    std::cout << "&m1 = " << &m1 << std::endl;
    std::cout << "&m2 = " << &m2 << std::endl;
    std::cout << "m1.data = " << m1.data << std::endl;
    std::cout << "m2.data = " << m2.data << std::endl;
    std::cout << "m1.data.get() = " << m1.data.get() << std::endl;
    std::cout << "m2.data.get() = " << m2.data.get() << std::endl;
   
    m1.setElement(1, 2, 4.5f);
    std::cout << m2.getElement(1,2) << std::endl;

    std::cout << m1 << std::endl;
    std::cout << m2 << std::endl;

    Matrix m3 = m1 + m2;
    std::cout << "&m1 = " << &m1 << std::endl;
    std::cout << "&m2 = " << &m2 << std::endl;
    std::cout << "&m3 = " << &m3 << std::endl;
    std::cout << "m1.data = " << m1.data << std::endl;
    std::cout << "m2.data = " << m2.data << std::endl;
    std::cout << "m3.data = " << m3.data << std::endl;
    std::cout << "m1.data.get() = " << m1.data.get() << std::endl;
    std::cout << "m2.data.get() = " << m2.data.get() << std::endl;
    std::cout << "m3.data.get() = " << m3.data.get() << std::endl;

    Matrix m4 = m1;
    std::cout << "&m1 = " << &m1 << std::endl;
    std::cout << "&m2 = " << &m2 << std::endl;
    std::cout << "&m3 = " << &m3 << std::endl;
    std::cout << "&m4 = " << &m4 << std::endl;
    std::cout << "m1.data = " << m1.data << std::endl;
    std::cout << "m2.data = " << m2.data << std::endl;
    std::cout << "m3.data = " << m3.data << std::endl;
    std::cout << "m4.data = " << m4.data << std::endl;
    std::cout << "m1.data.get() = " << m1.data.get() << std::endl;
    std::cout << "m2.data.get() = " << m2.data.get() << std::endl;
    std::cout << "m3.data.get() = " << m3.data.get() << std::endl;
    std::cout << "m4.data.get() = " << m4.data.get() << std::endl;



    std::cout << m3 << std::endl;
    std::cout << m4 << std::endl;
    


    return 0;
}

result:

Constructor Matrix(r, c) 
Constructor Matrix(r, c) 
&m1 = 0x16b8e2fb8
&m2 = 0x16b8e2f98
m1.data = 0x14ae05c70
m2.data = 0x14ae05cd0
m1.data.get() = 0x14ae05c70
m2.data.get() = 0x14ae05cd0
&m1 = 0x16b8e2fb8
&m2 = 0x16b8e2f98
m1.data = 0x14ae05c70
m2.data = 0x14ae05c70
m1.data.get() = 0x14ae05c70
m2.data.get() = 0x14ae05c70
4.5
size (3x8)
[
0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 4.5, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 
]
size (3x8)
[
0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 4.5, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 
]
Matrix Add 
Constructor Matrix(r, c) 
&m1 = 0x16b8e2fb8
&m2 = 0x16b8e2f98
&m3 = 0x16b8e2f68
m1.data = 0x14ae05c70
m2.data = 0x14ae05c70
m3.data = 0x14ae05d20
m1.data.get() = 0x14ae05c70
m2.data.get() = 0x14ae05c70
m3.data.get() = 0x14ae05d20
Constructor Matrix(M) 
&m1 = 0x16b8e2fb8
&m2 = 0x16b8e2f98
&m3 = 0x16b8e2f68
&m4 = 0x16b76ef48
m1.data = 0x14ae05c70
m2.data = 0x14ae05c70
m3.data = 0x14ae05d20
m4.data = 0x14ae05c70
m1.data.get() = 0x14ae05c70
m2.data.get() = 0x14ae05c70
m3.data.get() = 0x14ae05d20
m4.data.get() = 0x14ae05c70
size (3x8)
[
0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 9, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 
]
size (3x8)
[
0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 4.5, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 
]
Destructor Matrix 
Destructor Matrix 
Destructor Matrix 
Destructor Matrix 

你可能感兴趣的:(CPP,c++)