本文学习自 狄泰软件学院 唐佐林老师的 C++课程
实验1:无参构造函数 和 拷贝构造函数
实验2:
2.1 在类中自定了拷贝构造函数的请况下 创建无参对象 的冲突
2.2 在类中自定了有参构造函数的情况下 创建无参对象 的冲突
实验3:浅拷贝问题–涉及到堆空间会出现重复释放内存
实验4 :深拷贝应用:涉及到堆空间操作一定要用深拷贝
对象的构造函与内存操作
实验1:无参构造函数 和 拷贝构造函数
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
/*Test(const Test& t)
{
i = t.i;
j = t.j;
}
Test()
{
}*/
};
int main()
{
//类中没有自定义的构造函数,调用无参构造函数
Test t1;
//
Test t2 = t1;
printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());
return 0;
}
过程: C++编译器在发现我们定义 Test 类的时候 没有定义构造函数,编译器就会为我们主动添加一个无参构造函数。同样C++编译器也没有发现自定义的拷贝构造函数,于是编译器为我们自动添加了一个拷贝构造函数,编译器所提供的拷贝构造函数仅仅是将成员变量做一个简单的复制操作。
Test() //无参构造函数
{
}
Test(const Test& t) //拷贝构造函数
{
i = t.i;
j = t.j;
}
经典问题:在如下类中,有些什么东西
class T
{
}
答案: 至少有一个无参构造函数,哪儿来的? 编译器提供的!
实验2:
自定义拷贝构造函数
在类中自定了拷贝构造函数的请况下 创建无参对象 的冲突
在类中自定了有参构造函数的情况下 创建无参对象 的冲突
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
//自定义拷贝构造函数
Test(const Test& t)
{
i = t.i;
j = t.j;
}
};
int main()
{
Test t1;
Test t2 = t1;
printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());
return 0;
}
mhr@ubuntu:~/work/c++$ gcc 19-1.cpp
19-1.cpp: In function ‘int main()’:
19-1.cpp:26:10: error: no matching function for call to ‘Test::Test()’
Test t1;
^
19-1.cpp:17:5: note: candidate: Test::Test(const Test&)
Test(const Test& t)
^
19-1.cpp:17:5: note: candidate expects 1 argument, 0 provided
mhr@ubuntu:~/work/c++$
编译报错了!
no matching function for call to ‘Test::Test()
问题 : C++编译器提示 找不到 ‘Test::Test()’ 这个默认添加的无参构造函数了! 这是为什么呢? 之前不是说编译器会自动添加一个无参构造函数吗?
答案:编译器为我们提供 无参构造函数的条件是 当类中没有定义构造函数时,编译器才会为我们提供一个无参构造函数,然而此时我们的函数体内 有一个自定义的拷贝构造函数,拷贝构造函数也是构造函数!! 所以编译器认为 你已经在类中提供了一个构造函数,我就没必要为你提供任何的构造函数了,所以这时候程序就会因为没有无参构造函数而报错!!所以需要添加一个无参的构造函数。
同理当我们在类中自定了有参构造函数(如 一个in参数)后,直接 Test t1; 也会报错,因为编译器看到类中已经定义的有参构造函数,就不会自动添加无参构造函数了,所以直接 Test t1;汇报错,不能穿件无参对象,只能创建有参对象 如: Test t1(1);
改:
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
Test(const Test& t)
{
i = t.i;
j = t.j;
}
Test()
{
}
};
int main()
{
Test t1;
Test t2 = t1;
printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());
return 0;
}
mhr@ubuntu:~/work/c++$ gcc 19-1.cpp
mhr@ubuntu:~/work/c++$ ./a.out
t1.i = 0, t1.j = 0
t2.i = 0, t2.j = 0
mhr@ubuntu:~/work/c++$
初始化和赋值不同,因为初始化的时候起始会牵涉到拷贝构造函数的调用。
有了拷贝构造函数之后就可以用一个已经存在的对象来创建另一个新的对象,进而使得两个对象是一样的。
实验3:浅拷贝问题–涉及到堆空间会出现重复释放内存
#include <stdio.h>
class Test
{
private:
int i;
int j;
int* p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getP()
{
return p;
}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
};
int main()
{
Test t1(3);
Test t2=t1;
printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());
return 0;
}
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ g++ 19-2.cpp
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ ./a.out
t1.i = 1, t1.j = 2, t1.p = 0x2039c20
t2.i = 1, t2.j = 2, t2.p = 0x2039c20
mhr@ubuntu:~/work/c++$
分析:
看似没有问题 两个对象的所有成员都是一样的,但是! 两个对象的指针成员指向了同一块内存!! 这是有巨大的问题的!!,我们在堆空间申请到内存后需要释放,这时候两个对象就是释放两次堆空间的同一块内存,会出错!!
改:添加free()
#include <stdio.h>
class Test
{
private:
int i;
int j;
int* p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getP()
{
return p;
}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(3);
Test t2=t1;
printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());
t1.free();
t2.free();
return 0;
}
mhr@ubuntu:~/work/c++$ g++ 19-2.cpp
mhr@ubuntu:~/work/c++$ ./a.out
t1.i = 1, t1.j = 2, t1.p = 0x18e4c20
t2.i = 1, t2.j = 2, t2.p = 0x18e4c20
*** Error in `./a.out': double free or corruption (fasttop): 0x00000000018e4c20 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7fd013503725]
/lib/x86_64-linux-gnu/libc.so.6(+0x7ff4a)[0x7fd01350bf4a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fd01350fabc]
./a.out[0x400884]
./a.out[0x4007c7]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fd0134ac830]
./a.out[0x400629]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 12848233 /home/mhr/work/c++/a.out
00600000-00601000 r--p 00000000 08:01 12848233 /home/mhr/work/c++/a.out
00601000-00602000 rw-p 00001000 08:01 12848233 /home/mhr/work/c++/a.out
018d3000-01905000 rw-p 00000000 00:00 0 [heap]
7fd00c000000-7fd00c021000 rw-p 00000000 00:00 0
7fd00c021000-7fd010000000 ---p 00000000 00:00 0
7fd012f6d000-7fd012f83000 r-xp 00000000 08:01 266720 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd012f83000-7fd013182000 ---p 00016000 08:01 266720 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd013182000-7fd013183000 rw-p 00015000 08:01 266720 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd013183000-7fd01328b000 r-xp 00000000 08:01 266752 /lib/x86_64-linux-gnu/libm-2.23.so
7fd01328b000-7fd01348a000 ---p 00108000 08:01 266752 /lib/x86_64-linux-gnu/libm-2.23.so
7fd01348a000-7fd01348b000 r--p 00107000 08:01 266752 /lib/x86_64-linux-gnu/libm-2.23.so
7fd01348b000-7fd01348c000 rw-p 00108000 08:01 266752 /lib/x86_64-linux-gnu/libm-2.23.so
7fd01348c000-7fd01364c000 r-xp 00000000 08:01 266682 /lib/x86_64-linux-gnu/libc-2.23.so
7fd01364c000-7fd01384b000 ---p 001c0000 08:01 266682 /lib/x86_64-linux-gnu/libc-2.23.so
7fd01384b000-7fd01384f000 r--p 001bf000 08:01 266682 /lib/x86_64-linux-gnu/libc-2.23.so
7fd01384f000-7fd013851000 rw-p 001c3000 08:01 266682 /lib/x86_64-linux-gnu/libc-2.23.so
7fd013851000-7fd013855000 rw-p 00000000 00:00 0
7fd013855000-7fd0139c7000 r-xp 00000000 08:01 4465698 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd0139c7000-7fd013bc7000 ---p 00172000 08:01 4465698 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd013bc7000-7fd013bd1000 r--p 00172000 08:01 4465698 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd013bd1000-7fd013bd3000 rw-p 0017c000 08:01 4465698 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd013bd3000-7fd013bd7000 rw-p 00000000 00:00 0
7fd013bd7000-7fd013bfd000 r-xp 00000000 08:01 266654 /lib/x86_64-linux-gnu/ld-2.23.so
7fd013ddf000-7fd013de4000 rw-p 00000000 00:00 0
7fd013df9000-7fd013dfc000 rw-p 00000000 00:00 0
7fd013dfc000-7fd013dfd000 r--p 00025000 08:01 266654 /lib/x86_64-linux-gnu/ld-2.23.so
7fd013dfd000-7fd013dfe000 rw-p 00026000 08:01 266654 /lib/x86_64-linux-gnu/ld-2.23.so
7fd013dfe000-7fd013dff000 rw-p 00000000 00:00 0
7ffeebf52000-7ffeebf73000 rw-p 00000000 00:00 0 [stack]
7ffeebfcf000-7ffeebfd1000 r--p 00000000 00:00 0 [vvar]
7ffeebfd1000-7ffeebfd3000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted (core dumped)
mhr@ubuntu:~/work/c++$
堆空间中的同一块内存被释放了两次,结果很明显,运行后出现内存错误!
实验4 :深拷贝应用:涉及到堆空间操作一定要用深拷贝
#include <stdio.h>
class Test
{
private:
int i;
int j;
int* p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getP()
{
return p;
}
//深拷贝
Test(const Test& t)
{
i = t.i;
j = t.j;
p = new int;//p指针不会赋值 而是会重新向堆空间申请
*p = *t.p;//将 t1对象p指针指向堆空间内存中的数据 拷贝到 新的对象所申请的堆空间中
}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(3);
Test t2(t1);//Test t2 = t1;
printf("t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *t1.getP());
printf("t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *t2.getP());
t1.free();
t2.free();
return 0;
}
mhr@ubuntu:~/work/c++$ g++ 19-2.cpp
mhr@ubuntu:~/work/c++$ ./a.out
t1.i = 1, t1.j = 2, t1.p = 0x1ee2c20, *t1.p = 3
t2.i = 1, t2.j = 2, t2.p = 0x1ee2c40, *t2.p = 3
mhr@ubuntu:~/work/c++$
结果分析:
两个对象中的指针成员,地址不同,但是指向地址中所存储的数据是相同的。成功!
关于浅拷贝指针 重复free的问题 如下: