c++之构造函数深究_匿名对象_new_this初探


站在编译器和C的角度剖析c++原理, 用代码说话


默认构造函数

我们先回顾一下构造函数, 当我们没有指定带参的构造函数时,编译器会给我们的类自动分配一个默认的无参构造函数.怼代码:

class Test{
public:
    Test(){
        cout << "我是构造函数,自动被调用了" << endl;
    }
    ~Test(){
        cout << "我是析构函数,自动被调用了";
    }
protected:
private:
    int a;
};
//单独搭建一个舞台,让对象唱戏, 这样可以完整的看见对象声明周期的呈现
void ObjPlay()
{
    Test t1;
    cout << "展示t1的生命周期" << endl;
}
int main(int argc, const char * argv[]) {
    ObjPlay();
    return 0;
}

~Test()这个表示的就是析构函数,当 Test t1;的作用域执中的代码执行完后就开始调用析构函数进行资源回收. 这只是个简单的例子,我们接下来会陆续有复杂的例子,跟进喽~~.

拷贝构造函数

copy构造函数又叫做复制构造函数, 这是重点也是个难点,也是C++的特有的重要特点,我会循序渐进的讲解清晰. 首先要知道拷贝构造函数也是一种构造函数,也就是说如果我们没有指定代参的构造函数而是指定了拷贝构造函数,那么系统也不再会给我们分配默认的无参构造函数.
我们上代码进行分析:

class AA
{
public:
    AA()
    {
        a = 5;
        cout<<"我是构造函数,自动被调用了"<<endl;
    }
    AA(const AA &obj2)
    {
        cout<<"我也是构造函数,我是通过另外一个对象obj2,来初始化我自己"<<endl;
        a = obj2.a + 10;
    }
    ~AA()
    {
        cout<<"我是析构函数,自动被调用了"<<endl;
    }
    void getA()
    {
        printf("a:%d \n", a);
    }
protected:
private:
    int a;
};
void ObjPlay01()
{
    AA a1; 
    AA a2 = a1; 
    //a2 = a1; 
}
int main()
{
    ObjPlay01();
    return 0;
}

我们通过拷贝构造函数的语法格式上可以看出const AA &obj2就是用一个对象初始化另一个对象. 首先AA a1;创建了一个AA对象, AA a2 = a1;当我们执行这句话的时候,就是用一个对象初始化了另外一个对象,注意初始化的意思就是边创建边赋值这种形式. 我们从内存中分析这一过程: 这里先说明一点,我们在后面会介绍到c++的操作符, 就是等号等等. c++的这些符号很有意思, 这里只涉及到了等号,那么我们就事论事. 这个等号很有意思. 那么这个过程是什么样的呢? AA a2 = a1;一执行,那么就会调用a2的拷贝构造函数, 在这之前的栈中已经有了a2对象的a属性了, 然后也有a1对象的a属性,并且a1对象的a属性已经是5了,因为a1执行了无参构造函数, 但是a2的a还是0. 那么a1就是obj2. 然后执行了运算后, this(也就是a2)对象的a已经变成了15,在这里我们发现a2创建的时候没有调用无参构造函数,这就是拷贝构造函数高效率的地方. 如果我们放开注释的a2 = a1那么这种等于号的操作不是对象初始化的操作,那么就是简单的值拷贝,a1栈中的值给了a2, 这也就是说的浅拷贝. 我们再看一种场景:

class AA
{
public:
    AA(int _a) //无参构造函数 默认构造函数
    {
        a = _a;
    }
    ~AA()
    {
        cout<<"我是析构函数,自动被调用了"<<endl;
    }
    void getA()
    {
        printf("a:%d \n", a);
    }
protected:
private:
    int a;
};
void ObjPlay02()
{
    AA a1(10); 
    AA a2(a1); //定义变量并初始化
    //a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy
    a2.getA();
}
int main()
{
    ObjPlay02();
    return 0;
}

在这种场景下我们是用AA a2(a1);这种方式进行初始化的,与上面的AA a2 = a1;是一样的. 并且这个案例中没有了拷贝构造函数,那么当我们执行AA a2(a1);时,按理说就应该去执行拷贝构造函数,那么编译器就会给我们隐士的走一个拷贝构造函数,但是这个函数也是简单浅拷贝,但是这样也就能使得第二个对象不需要走构造非拷贝构造函数的构造函数. 接下来就是析构了, 先初始化的后析构,所以会先析构a2,然后析构a1. 当然都是在执行完a2.getA()后执行的.
对拷贝构造函数有了初步认识后,我们再接个案例:

class Location
{
public:
    Location( int xx = 0 , int yy = 0 )
    {
        X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ;
    }
    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 ;
} ;
void f ( Location  p )
{
    cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl ;
}
void mainobjplay()
{
    Location A ( 1, 2 ) ;
    f ( A ) ;//调用了拷贝构造函数
}
int main()
{
    mainobjplay();
    return 0;
}

前面的就不重复了,f ( A ) ;这句话对应的函数中的Location p, 注意形参中的类型不是引用也不是指针,是一个对象,所以就和上面的初始化一样了,那么就直接调用拷贝构造函数就行. 然后析构两次,第一次是从void f这个函数跳出来也就是f(A)执行完的时候析构一次,也就是析构P,然后析构A.

匿名对象

对拷贝构造函数有了更进一步的认识后,我们需要引入一个叫匿名对象的东东,挺恶心的,跟紧了~~:

class Location
{
public:
    Location( int xx = 0 , int yy = 0 )
    {
        X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ;
    }
    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 ;
} ;
void f ( Location  p )
{
    cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl ;
}
Location g()//注意这里返回的不是指针不是引用, 是一个对象
{
    Location A(1, 2);
    return A;
}
void mainobjplay()
{
    Location B;
    B = g();
    //Location B = g();
int main()
{
    mainobjplay();
    return 0;
}

这段代码与之前的不同之处在于Location g()这个函数, 这里返回的不是引用也不是指针,返回的是个对象,但是一旦出了作用于就回收了啊,那么编译器和之前咱们讨论到的返回的东西一样,给临时区分配了一个这个对象的匿名对象,然后针对匿名对象,这里连析构都没调用,当用B=g()的时候,栈中的A对象析构,因为匿名对象还在,然后这个匿名对象并没有走拷贝构造函数,而是直接进行了等号的浅拷贝直接给了B对象的对应变量赋值.然后匿名对象直接挂掉. 这样我们只进行了两次有参构造函数和两次析构. 当我们用Location B = g();的时候,就总共进行了一次有参构造和一次析构. 这是为什么呢?不是应该A也要析构吗?这就是我上面稍微提到过的对象初始化问题,后者就是在对象初始化的时候进行匿名对象拷贝的,但是前者并不是初始化的情况. 当初始化的时候,相当于要析构的A上面覆盖了B了,然后返回的匿名对象进行浅拷贝. 之前为什么不是因为B在A之前已经有了,所以两者地址不一样.
接下来我们再加强一下拷贝构造函数的深浅拷贝问题:

class Name
{
public:
    Name(const char *pname)
    {
        size = strlen(pname);
        pName = (char *)malloc(size + 1);
        strcpy(pName, pname);
    }
    ~Name()
    {
        cout<<"开始析构"<<endl;
        if (pName!=NULL)
        {
            free(pName);
            pName = NULL;
            size = 0;
        }
    }
protected:
private:
    char *pName;
    int size;
};
void playObj()
{
    Name obj1("obj1.....");
    Name obj2 = obj1; //obj2创建并初始化
    Name obj3("obj3...");
    cout<<"业务操作。。。5000"<<endl;
}
int  main()
{
    playObj();
    return 0;
}

这段代码中我们没有指定拷贝构造函数,那么就会调动编译器提供的默认拷贝构造函数,那么当在执行第三次析构的时候(也就是析构obj1)的时候程序就崩了. 为什么?我上面也已经提到过了,默认的拷贝构造函数是浅拷贝,具体流程是这样的: 一开始执行到方法playObj的时候,栈中创建了obj1, obj2, obj3, 然后obj1开始调用有参构造函数并且pName中存储了堆区分配好的首地址. 然后进行初始化的时候调用拷贝构造函数,这是个浅拷贝,只是将栈内存中的值进行值进行了拷贝,也就是拷贝了那个堆区的首地址给了obj的pName. 换句话说就是它俩指向了同一块儿堆区. 然后obj3结束后先析构obj3,接着析构obj2,当析构obj1的时候堆中已经非法了.所以我们不能再用系统给的浅拷贝的默认拷贝构造函数了, 而是需要重新为2分配一块儿内存:

 Name(Name &obj)
    {
        //用obj来初始化自己
        pName = (char *)malloc(obj.size + 1);
        strcpy(pName, obj.pName);
        size = obj.size;
    }

当我们执行obj2 = obj3;的话,程序依然是蹦,原因就如上一下都是浅拷贝,所以我们需要对这个等号操作, 我们下一张就会涉及到对操作符的重写. 这里先题一下:

void operator=(Name &obj3)
    {
        if (pName != NULL)
        {
            free(pName);
            pName = NULL;
            size = 0;
        }
        cout<<"测试有没有调用我。。。。"<<endl;
        pName = (char *)malloc(obj3.size + 1);
        strcpy(pName, obj3.pName);
        size = obj3.size;
    }

这里有个问题就是到底有没有必要先释放一波内存,我觉得是没有必要的,有的人会说内存泄露问题,但是我通过内存观察并没有发现有内存泄露问题,并且我们obj2分配了新的内存,并没有影响到obj3的pName的指向问题啊,只是用了它里面的值啊. 所以说如果有大神看见批评两句然后呈上您的解析~~~~万分感谢………
到这里我们就把拷贝构造函数的情况基本涉及到了,那么总结一下吧:
拷贝构造函数规则研究:
1. 当类中没有定义任何一个构造函数的时候,c++编译器会提供无参构造函数和拷贝构造函数
2. 当类中定义了任意的非拷贝构造函数(无参,有参), c++编译器不会提供无参构造函数
3. 当类中定义了拷贝构造函数, c++编译器不会提供午餐构造函数
4. 默认的拷贝构造函数成员变量简单赋值.

构造函数的初始化列表

先上代码其中就包含了语法规则:

class A
{
public:
    A(int _a1)
    {
        a1 = _a1;
    }
protected:
private:
    int a1;
};
//构造函数的初始化列表产生原因
//语法现象
class B
{
public:
    B():mya(12),mya2(100)
    {
        ;
    }
    //成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
    B(int x, int y):mya(y),mya2(101)
    {
        b1 = x;
    }
protected:
private:
    int b1;
    A mya2;
    A mya;

};
int main()
{
    A a1(10);
    B b1(10, 20);
    return 0;
}

当我们在初始化B的时候,B中的成员属性有类A, 所有需要对A进行初始化,这样就出现了初始化列表. 执行步骤是先初始化mya2(101), 接着mya(y),最后b1 = x;. 当析构的时候,先析构自己本身,然后析构A mya, 最后析构A mya2.
初始化列表我们再来一段代码然后总结一下:

class ABC
{
public:
    ABC(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        printf("a:%d,b:%d,c:%d \n", a, b, c);
        printf("ABC construct ..\n");
    }
    ~ABC()
    {
        printf("a:%d,b:%d,c:%d \n", a, b, c);
        printf("~ABC() ..\n");
    }
protected:
private:
    int a;
    int b;
    int c;
};
class MyD
{
public:
    MyD():abc1(1,2,3),abc2(4,5,6),m(100)
    {
        cout<<"MyD()"<<endl;
    }
    ~MyD()
    {
        cout<<"~MyD()"<<endl;
    }
protected:
private:
    ABC abc1; //c++编译器不知道如何构造abc1
    ABC abc2;
    const int m;
};

int run()
{
    MyD myD;
    return 0;
}
int main()
{
    run();
    return 0;
}

使用初始化列表出现原因:
1. 如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
2. 类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值, 当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

拷贝构造函数加强

先上案例:

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;
    }
protected:
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; 
    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);//会执行拷贝构造函数
    return 0;
}
int run3()
{
    printf("run3 start..\n");
    ABCD abcd = ABCD(100, 200, 300);
    //ABCD(400, 500, 600); //临时对象的生命周期, 构造函数后直接就执行析构了,因为这个临时对象c++发现没用.
    printf("run3 end\n");
    return 0;
}
int main()
{
    //run2();
    run3();
    return 0;
}

run2()就不进行解释了,run3()中ABCD abcd = ABCD(100, 200, 300);就是典型的用匿名对象进行初始化, 这样,只执行一次有参构造函数,然后一次析构,因为这是初始化. 置于后面注释的ABCD(400, 500, 600);如果只执行这句的话,就是进行有参构造后,直接进行了析构,因为编译器发现这个匿名对象并没有用,所以会直接析构.
就像这段代码:

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", t1.getC()); //乱码
    system("pause");
    return 0;
}

为什么会乱码呢,因为MyTest(a, b, 100);这个匿名函数编译器发现并没有用到所以直接进行了析构. 所以对c根本没有操作.

new delete

先上案例进行分析:

int main()
{
    //new基础类型
    int *p = (int *)malloc(sizeof(int));
    free(p);

    int *p2 = new int; //相当于上面的

    *p2 = 101;
    printf("*p2:%d \n", *p2);
    delete p2;

    //分配内存的同时,初始化
    int *p3 = new int(100);
    delete p3;
    return 0;
}
int main()
{
    //new数组
    int *p = (int *)malloc(10*sizeof(int));  //int a[10];
    p[0] = 1;
    free(p);
    int *p2 = new int[10] ;//int a[10];
    p2[0] = 1;
    p2[1] = 2;
    delete [] p2;
    return 0;
}
//new 对象
class Test
{
public:
    Test(int mya, int myb)
    {
        a = mya;
        b = myb;
        cout<<"构造函数,我被调用了"<<endl;
    }
    ~Test()
    {
        cout<<"析构函数,我被调用了"<<endl;
    }
    int getA()
    {
        return a;
    }
protected:
private:
    int a;
    int b;
};
int getTestObj(Test **my)
{
    Test *p = new Test(1, 2);
    *my = p;
    return 0;
}
int main()
{
    //new + 类型  返回一个 内存首地址
    //new操作符也会自动的调用这个类的构造函数
    //delete自动的调用这个类的析构函数

    //相当于我们程序员可以手工控制类的对象的生命周期
    Test *p = new Test(1, 2);//这个内存分配在了堆上
    cout<<p->getA()<<endl;

    getTestObj(&p);//我们也可以传入一个Test指针然后让函数去帮我们初始化对象
    delete p;
    return 0;
}

malloc不会调用这个类的构造函数.

static

上案例代码:

class BB
{
public:
    int getC()
    {
        return c;
    }
    void setC(int nyc)
    {
        c = nyc;  
    }
    //静态成员函数是属于整个类,
    static void getMem()
    {
        //cout<<a<<endl;是因为不知道输出哪个对象的a,这个a应该是属于具体的对象的a, 但是getMem是全局的
        cout<<c<<endl;
    }
protected:
private:
    int a;
    int b;
    static int c;
};
int BB::c = 0;
//static修饰的变量,是属于类,所有的对象都能共享用。
void main111()
{
    BB b1;
    BB b2;
    cout<<b2.getC()<<endl;;
    b1.setC(100);
    cout<<b2.getC()<<endl;;
    system("pause");
}
int main()
{
    //调用静态成员函数的方法1
    BB::getMem(); 
    //调用静态成员函数的方法2
    BB b1;
    b1.getMem();
    return 0;
}

由上面代码我们可以知道,静态成员函数是属于整个类, static修饰的变量,是属于类,所有的对象都能共享用. 当使用静态成员函数的时候,操作的成员变量也必须是静态的,因为静态成员函数是放在静态区的,并不是和类放在栈中的,所以当我们创建不同的类的时候,静态的成员函数和成员变量并不会变化,所以不同的对象调用的时候,它们区分不出来是哪个对象在调用,所以静态成员函数只能使用静态成员变量. 再有就是调用静态成员函数的方法其中一种是:BB::getMem();,你或许会有疑问说这谁调用的啊,显示谁的啊? 记住这是静态成员,你什么时候调用就是直接调用静态区的值. 没有关联任何的对象.
有人会问说类的内存四区示意图是什么样子呢?我们在后面有一个专题会讨论这个,这里先给大家测试一下类到底占用多大内存:

class C1
{
public:
    int i; //4
    int j; //4
    int k; //4
protected:
private:
}; //12
class C2
{
public:
    int i; //4
    int j; //4
    int k; //4

    static int m; 
public:
    int getK() const { return k; }
    void setK(int val) { k = val; }
protected:
private:
}; 
int main()
{
    C2 c21, C22,C23;
    c21.getK();
    //c22.getK();
    printf("sizeof(C1):%d \n", sizeof(C1));
    printf("sizeof(C2):%d \n", sizeof(C2));
    return 0;
}

C1的大小就不说了,C2的大小也是12, 这说明了成员变量和成员函数不在一起分配的.
成员变量:
普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式
静态成员变量:存储于全局数据区中
成员函数:存储于代码段中。
问题出来了:很多对象公用一块代码?代码是如何区分具体对象的那?引出了我们接下来的话题

this 初探

学到这里,那么我们有个疑问,当我们调用类中的成员方法的时候用b1.getCount()这种方式,不同的对象都用这种方式去调用,那么类中是如何识别是属于哪个类的呢?
这里写图片描述

class Test
{
public:
    Test(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void getA() //getA(this);
    {
        printf("a:%d \n", this->a);
        printf("a:%d \n", a);
    }
protected:
private:
    int a;
    int b;
};
int main()
{
    Test t(1,2); //====>Test(this, 1, 2);===>Test(&t, 1, 2);
    return 0;
}
  1. C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
  2. C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
  3. 静态成员函数、成员变量属于类
    静态成员函数与普通成员函数的区别
    静态成员函数不包含指向具体对象的指针
    普通成员函数包含一个指向具体对象的指针

联系方式: reyren179@gmail.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在Java中,拷贝构造函数并不像C++中那样直接支持。但我们可以通过实现一个类的 clone() 方法来实现拷贝构造函数的功能。clone() 方法是 Java 中提供的一种浅拷贝(Shallow Copy)的方法,它可以用于创建一个对象的拷贝。 下面是一个示例代码,演示了如何实现一个拷贝构造函数: ``` public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // 拷贝构造函数 public Person(Person another) { this.name = another.name; this.age = another.age; } // clone() 方法 public Person clone() { try { return (Person) super.clone(); } catch (CloneNotSupportedException e) { // 如果类没有实现 Cloneable 接口,则会抛出该异常 return null; } } } ``` 在上面的代码中,我们定义了一个 Person 类,并实现了一个拷贝构造函数。拷贝构造函数接受一个 Person 类型的参数,用于创建一个新的 Person 对象,该对象与传入的参数对象具有相同的属性值。 此外,我们还实现了 clone() 方法,该方法可以用于创建一个 Person 对象的拷贝。在该方法中,我们调用了 Object 类中的 clone() 方法,并将其返回类型转换为 Person 类型。 需要注意的是,clone() 方法是 Object 类中的方法,如果需要使用该方法,必须在 Person 类中实现 Cloneable 接口。如果没有实现该接口,则在调用 clone() 方法时会抛出 CloneNotSupportedException 异常。 ### 回答2: 在Java中,拷贝构造函数是一种特殊的构造函数,它用于创建一个对象的副本。拷贝构造函数允许我们将一个对象的值复制到另一个对象中,而不是仅仅复制其引用。这在需要克隆或复制对象时非常有用。 要创建一个拷贝构造函数,我们需要在类中定义一个与类名相同的方法,并传入一个参数为该类的对象。例如,假设我们有一个名为Person的类,其中包含name和age两个属性。 public class Person { private String name; private int age; public Person(Person other) { this.name = other.name; this.age = other.age; } } 在上面的例子中,我们创建了一个在类中定义的拷贝构造函数。在该函数中,我们使用另一个Person对象的属性值来初始化新对象的属性。这样一来,我们就可以通过传递一个现有的Person对象来创建一个新的Person对象。 使用拷贝构造函数有几个好处。首先,它允许我们创建一个新对象,该对象与现有对象具有相同的属性值。这对于不修改现有对象但需要创建新对象的情况非常有用。其次,它避免了浅拷贝(只复制引用而不复制实际值)可能导致的问题。 在Java中,拷贝构造函数的使用是可选的。如果我们不定义一个拷贝构造函数,Java默认提供一个浅拷贝的拷贝构造函数。但是,如果我们需要执行深拷贝(复制对象及其所有子对象的值),就需要自己定义拷贝构造函数。 总之,拷贝构造函数是在Java中创建一个对象副本的一种特殊函数。它允许我们通过将一个对象的属性值复制到另一个对象中来创建新对象。这在复制、克隆对象或创建新对象时非常有用。 ### 回答3: 在Java中,拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,该对象的内容与现有对象相同。拷贝构造函数的主要作用是复制一个对象的值到另一个新的对象中。 在Java中,拷贝构造函数的定义如下: public class MyClass { private int myInt; // 拷贝构造函数 public MyClass(MyClass obj) { this.myInt = obj.myInt; } // 构造函数 public MyClass(int myInt) { this.myInt = myInt; } // Getter和Setter方法 public int getMyInt() { return myInt; } public void setMyInt(int myInt) { this.myInt = myInt; } } 在上面的示例中,我们定义了一个名为MyClass的类,该类具有一个整型成员变量myInt。我们使用两个构造函数来创建对象,一个是拷贝构造函数,一个是普通构造函数。 拷贝构造函数的参数类型是与该类相同的对象,通过该参数将现有对象的值复制到新对象中。在拷贝构造函数中,我们可以使用this关键字引用当前对象,使用点操作符(.)访问对象的成员变量。 使用拷贝构造函数创建对象的示例代码如下: MyClass obj1 = new MyClass(10); // 使用普通构造函数创建对象 MyClass obj2 = new MyClass(obj1); // 使用拷贝构造函数创建对象 System.out.println(obj1.getMyInt()); // 输出:10 System.out.println(obj2.getMyInt()); // 输出:10 通过以上代码,我们可以看到obj2对象中的myInt值与obj1对象中的myInt值相同,这是因为拷贝构造函数将obj1对象的值复制给了obj2对象。 总之,拷贝构造函数是一种创建一个新对象并复制现有对象值的常用技术,它在Java中的定义和使用方法如上所述。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值