avatar
C++含有指针成员的类# Programming - 葵花宝典
y*b
1
通常destructor删除这个指针以释放内存。
但是现在这个指针指向的是一个forward declared的类,结果gcc提示:
neither the destructor nor the class-specific operator delete will be called
, even if they are declared when the class is defined
就是说这种情况无法释放内存?不仅如此,连行为都undefined?
avatar
p*o
2
forward declare的类通常不该这个类删。学着用智能指针吧。

called

【在 y**b 的大作中提到】
: 通常destructor删除这个指针以释放内存。
: 但是现在这个指针指向的是一个forward declared的类,结果gcc提示:
: neither the destructor nor the class-specific operator delete will be called
: , even if they are declared when the class is defined
: 就是说这种情况无法释放内存?不仅如此,连行为都undefined?

avatar
b*i
3
是吗?有代码吗?

called

【在 y**b 的大作中提到】
: 通常destructor删除这个指针以释放内存。
: 但是现在这个指针指向的是一个forward declared的类,结果gcc提示:
: neither the destructor nor the class-specific operator delete will be called
: , even if they are declared when the class is defined
: 就是说这种情况无法释放内存?不仅如此,连行为都undefined?

avatar
y*b
5
感觉C++这点有点问题:A类用到B类,B类也用到A类,总有一个要forward declaration
,就会出现这个问题。感觉一个语言不应该这样啊。
当然我可以改变设计,把互相作用的部分设计成一个新类,可是这样:
1.物理概念上就不算好。虽然抽象上不能说不算好,也可能不好,也可能很好。
2.就算能够,重构代码也很麻烦。C++还是需要module。
智能指针我试试。

【在 p***o 的大作中提到】
: forward declare的类通常不该这个类删。学着用智能指针吧。
:
: called

avatar
b*s
6
这是c++里面为了兼容c做的牺牲

declaration

【在 y**b 的大作中提到】
: 感觉C++这点有点问题:A类用到B类,B类也用到A类,总有一个要forward declaration
: ,就会出现这个问题。感觉一个语言不应该这样啊。
: 当然我可以改变设计,把互相作用的部分设计成一个新类,可是这样:
: 1.物理概念上就不算好。虽然抽象上不能说不算好,也可能不好,也可能很好。
: 2.就算能够,重构代码也很麻烦。C++还是需要module。
: 智能指针我试试。

avatar
b*i
7
我怎么觉得你如果按照C++通常的写法不会有问题的。我说的就是把类的定义写全,而
不是仅forward声明。所以你不需要智能指针。

declaration

【在 y**b 的大作中提到】
: 感觉C++这点有点问题:A类用到B类,B类也用到A类,总有一个要forward declaration
: ,就会出现这个问题。感觉一个语言不应该这样啊。
: 当然我可以改变设计,把互相作用的部分设计成一个新类,可是这样:
: 1.物理概念上就不算好。虽然抽象上不能说不算好,也可能不好,也可能很好。
: 2.就算能够,重构代码也很麻烦。C++还是需要module。
: 智能指针我试试。

avatar
p*o
8
1 std::shared_ptr使用的时候不需要完整类型。
2 上面那个so连接里连copy ctor/assignment都不知道要写,还是老老用智能指针把。

【在 b***i 的大作中提到】
: 我怎么觉得你如果按照C++通常的写法不会有问题的。我说的就是把类的定义写全,而
: 不是仅forward声明。所以你不需要智能指针。
:
: declaration

avatar
p*o
9
那个例子其实你把dtor的定义放到cpp里就可以编译过。
只是例子本身问题很多,没啥参考价值。

declaration

【在 y**b 的大作中提到】
: 感觉C++这点有点问题:A类用到B类,B类也用到A类,总有一个要forward declaration
: ,就会出现这个问题。感觉一个语言不应该这样啊。
: 当然我可以改变设计,把互相作用的部分设计成一个新类,可是这样:
: 1.物理概念上就不算好。虽然抽象上不能说不算好,也可能不好,也可能很好。
: 2.就算能够,重构代码也很麻烦。C++还是需要module。
: 智能指针我试试。

avatar
y*b
10
前面说了A,B两个类互用,无法避免forward declaration,否则也没这个问题。

【在 b***i 的大作中提到】
: 我怎么觉得你如果按照C++通常的写法不会有问题的。我说的就是把类的定义写全,而
: 不是仅forward声明。所以你不需要智能指针。
:
: declaration

avatar
y*b
11
只是搜个例子说明概念,代码就不深究。
我局部改用了shared_ptr,内存使用略有改进。因为涉及mpi、boost、指针序列化/反
序列化,回头贴点代码请大家看看。
先请教一个常识,main()函数里面有一个循环,每次循环都创建一些shared_ptr(或者
存储这些指针的vector),那么进入下次循环后,上次循环创建的shared_ptr会自动释
放吗?
或者说怎么理解shared_ptr的lifetime

【在 p***o 的大作中提到】
: 1 std::shared_ptr使用的时候不需要完整类型。
: 2 上面那个so连接里连copy ctor/assignment都不知道要写,还是老老用智能指针把。

avatar
b*i
12
能把代码写出来吗?没遇到这个问题啊
//.h
class CLB;
class CLA {
private:
int value;
CLB * b;
public:
CLA() {}
CLA(int v, bool forward);
virtual ~CLA();
};
//.cpp
CLA::CLA(int v, bool forward) {
value = v;
if (forward)
b = new CLB(v - 1, false);
else
b = NULL;
}
CLA::~CLA() {
if (b)
delete b;
cout << "done with " <}
// main.cpp
int main()
{
CLA *a = new CLA(10, true);
CLB *b = new CLB(111, true);
delete a;
delete b;
cin.get();
return 0;
}

【在 y**b 的大作中提到】
: 前面说了A,B两个类互用,无法避免forward declaration,否则也没这个问题。
avatar
y*b
13
CLA构造函数加入forward判断,不是好主意吧。另外,去掉这个判断不就提示了吗。
avatar
y*b
14
研究了一下智能指针,在这里唯一的好处就是不需要完整类型了。
如果有完整类型,没看出一般情况下智能指针有什么好处,直接管理内置指针很健壮。
实际上智能指针因为引用计数的缘故,大大增加了复杂程度:
shared_ptr如果作为局部变量,在超出作用域时被销毁;如果是唯一引用,则其所指向
的对象也被销毁,所占用的内存会被释放;如果不是唯一引用,则内存不会释放。你看
这有多复杂。
举个例子,一个类有这样一个成员变量:含有智能指针shared_ptr的向量。那么不同成
员函数按一定顺序使用、拷贝或赋值这(整)个向量,会导致引用计数反反复复变化,指
针什么时候存在,什么时候销毁,简直是个灾难了。比如在一个成员函数里面生成一个
新的智能指针,那么在该成员函数之外该智能指针被释放;若将该智能指针加入到向量
,则在该成员函数之外该智能指针不被释放。这种情况,还不如管理内置指针清晰健壮
啊。而且还要保证shared_ptr在无用之后进行手动释放,否则浪费内存。
感觉智能指针只有保证销毁自己一个好处,带来的底层状态变化实在够繁复,没见比直
接管理内置指针更容易。

【在 p***o 的大作中提到】
: 1 std::shared_ptr使用的时候不需要完整类型。
: 2 上面那个so连接里连copy ctor/assignment都不知道要写,还是老老用智能指针把。

avatar
b*i
15
用forward就是举个例子,防止无限循环构造,难道你要无限循环构造?
要么改成可以控制A一个实例的指针成员生成B实例,B实例的指针成员再生成新的A实例
,但是一定要有个限制。
//.h
class CLB;
class CLA {
private:
int value;
CLB * b;
public:
CLA() {}
CLA(int v, int level);
virtual ~CLA();
};
//.cpp
#include "CLA.h"
#include "CLB.h"
#include
using namespace std;
CLA::CLA(int v, int level) {
value = v;
if (level)
b = new CLB(v - 1, level-1);
else
b = NULL;
}
CLA::~CLA() {
if (b)
delete b;
cout << "done with " <}
编译运行没有任何问题。
还是你CLA 的变量a它的成员指向CLB的变量b,它的成员又指向原来的a?
所以,归根结底,要把你的代码拿出来,还有你的目的。

【在 y**b 的大作中提到】
: CLA构造函数加入forward判断,不是好主意吧。另外,去掉这个判断不就提示了吗。
avatar
p*o
16
封装好了你操什么心,现在有move语义,智能指针和容器都有优化,再说你不是在问
没完整类型怎么delete吗?
另外直接管理内置指针对新手就是个灾难。能记得写copy ctor/assignment的都算
不错的,写不写得对就另说了,好在C++11里还能delete掉。

【在 y**b 的大作中提到】
: 研究了一下智能指针,在这里唯一的好处就是不需要完整类型了。
: 如果有完整类型,没看出一般情况下智能指针有什么好处,直接管理内置指针很健壮。
: 实际上智能指针因为引用计数的缘故,大大增加了复杂程度:
: shared_ptr如果作为局部变量,在超出作用域时被销毁;如果是唯一引用,则其所指向
: 的对象也被销毁,所占用的内存会被释放;如果不是唯一引用,则内存不会释放。你看
: 这有多复杂。
: 举个例子,一个类有这样一个成员变量:含有智能指针shared_ptr的向量。那么不同成
: 员函数按一定顺序使用、拷贝或赋值这(整)个向量,会导致引用计数反反复复变化,指
: 针什么时候存在,什么时候销毁,简直是个灾难了。比如在一个成员函数里面生成一个
: 新的智能指针,那么在该成员函数之外该智能指针被释放;若将该智能指针加入到向量

avatar
y*b
17
智能指针和std::move都是好东西,没有疑义,我要尽量熟悉使用。c++11出来很久了,
一直没空学习,感觉好东西还不少。
话说java怎么就没这么复杂呢,纯粹是因为c++对性能有高要求的缘故吗?

【在 p***o 的大作中提到】
: 封装好了你操什么心,现在有move语义,智能指针和容器都有优化,再说你不是在问
: 没完整类型怎么delete吗?
: 另外直接管理内置指针对新手就是个灾难。能记得写copy ctor/assignment的都算
: 不错的,写不写得对就另说了,好在C++11里还能delete掉。

avatar
b*i
19
本来就该是这样吧。当一个对象还有人用的时候,当然不能销毁。
另外,你还有没有例子不用smartpointer,而无法编译?还是现在可以编译了(两个类
互相需要)?

【在 y**b 的大作中提到】
: 看个小例子:
: #include
: #include
: #include
: struct A
: {
: ~A() {std::cout << "Came to ~A()\n";}
: };
: int main(int argc, char** argv)
: {

avatar
y*b
20
问题是怎么判断还有人用呢,ref_count已经为零了

【在 b***i 的大作中提到】
: 本来就该是这样吧。当一个对象还有人用的时候,当然不能销毁。
: 另外,你还有没有例子不用smartpointer,而无法编译?还是现在可以编译了(两个类
: 互相需要)?

avatar
b*i
21
我觉得应该是指针还有效的才能看到计数。
试验两句就懂了
在第一个{}里面去掉把ptr2加入的语句。可以看到,ptr2所指向的对象在reset那里被
销毁了
然后第二个{}那里,
std::cout << " but list1 has " << ptrList[0].use_count() << std::endl;
虽然ptr1不能访问那个A对象实例了,在vector哪里呢。所以不能执行析构函数。
那个不用smartptr的例子呢?

【在 y**b 的大作中提到】
: 问题是怎么判断还有人用呢,ref_count已经为零了
avatar
y*b
22
这个自然,就是不明白编译器怎么判断ptr1还在vector里面呢,总之reset有点特别,
或者智能指针实现有点特别。
如果是个内置指针,放入容器,然后delete该指针,会调用destructor,这个与上面的
行为不一样。代码如下:
int main(int argc, char** argv)
{
A *ptr1 =new A();
A *ptr2 = new A();
std::vector ptrList;
{
std::cout << "In the nested scope 1n";
ptrList.push_back(ptr1);
ptrList.push_back(ptr2);
}
{
std::cout << "In the nested scope 2n";
delete ptr1;
delete ptr2;
}
{
std::cout << "In the nested scope 3n";
ptrList.clear();
}
std::cout << "Out of the nested scopen";
return 0;
}
输出:
In the nested scope 1
In the nested scope 2
Came to ~A()
Came to ~A()
In the nested scope 3
Out of the nested scope

【在 b***i 的大作中提到】

: 我觉得应该是指针还有效的才能看到计数。
: 试验两句就懂了
: 在第一个{}里面去掉把ptr2加入的语句。可以看到,ptr2所指向的对象在reset那里被
: 销毁了
: 然后第二个{}那里,
: std::cout << " but list1 has " << ptrList[0].use_count() << std::endl;
: 虽然ptr1不能访问那个A对象实例了,在vector哪里呢。所以不能执行析构函数。
: 那个不用smartptr的例子呢?

avatar
y*b
23
我那个代码太庞大,没法贴。弄个小的,可以重现一楼的编译提示:
// A.h
#ifndef A_H
#define A_H
#include "B.h"
class A {
public:
void test(B *b) {}
private:
int a;
B b;
};
#endif
// B.h
#ifndef B_H
#define B_H
class A;
class B {
public:
~B() {delete a;}
private:
A *a;
};
#endif
// main.cpp
#include "A.h"
#include "B.h"
int main() {
A *pa = new A();
B *pb = new B();
delete pa;
delete pb;
return 0;
}
编译提示:
B.h:6:16: note: neither the destructor nor the class-specific operator
delete will be called, even if they are declared when the class is defined
~B() {delete a;}

【在 b***i 的大作中提到】
: 我觉得应该是指针还有效的才能看到计数。
: 试验两句就懂了
: 在第一个{}里面去掉把ptr2加入的语句。可以看到,ptr2所指向的对象在reset那里被
: 销毁了
: 然后第二个{}那里,
: std::cout << " but list1 has " << ptrList[0].use_count() << std::endl;
: 虽然ptr1不能访问那个A对象实例了,在vector哪里呢。所以不能执行析构函数。
: 那个不用smartptr的例子呢?

avatar
p*o
24
ptr1都reset了, 自然相当于空指针, use_count是0。
你把vector里面那两个指针的use_count打出来就明白了。

【在 y**b 的大作中提到】
: 看个小例子:
: #include
: #include
: #include
: struct A
: {
: ~A() {std::cout << "Came to ~A()\n";}
: };
: int main(int argc, char** argv)
: {

avatar
p*o
25
前面说了,做个B.cpp,里面include A.h, B.h,再把B的dtor放到里面就好。

【在 y**b 的大作中提到】
: 我那个代码太庞大,没法贴。弄个小的,可以重现一楼的编译提示:
: // A.h
: #ifndef A_H
: #define A_H
: #include "B.h"
: class A {
: public:
: void test(B *b) {}
: private:
: int a;

avatar
y*b
26
是的。
但是怎么在nested scope 3还调用destructor呢
In the nested scope 3
Came to ~A()
Came to ~A()

【在 p***o 的大作中提到】
: ptr1都reset了, 自然相当于空指针, use_count是0。
: 你把vector里面那两个指针的use_count打出来就明白了。

avatar
p*o
27
scope 3你clear了vector,里面的shared_ptr析构,两个count都减到0,
所以会调A的dtor。

【在 y**b 的大作中提到】
: 是的。
: 但是怎么在nested scope 3还调用destructor呢
: In the nested scope 3
: Came to ~A()
: Came to ~A()

avatar
b*i
28
我觉得, 你前面ptr1是一个智能指针对象实例,然后构造一个A的实例,让ptr1来管理
,那么A现在的计数为1。当你把 ptr1通过push_back试图放到ptrList里面时,ptrList
[0]出现一个智能指针的实例,把A实例的计数加到2.
等你删掉ptr1时,虽然ptr1.use_count是0,那是因为你把它删掉了,所以它不知道指
向哪个对象了。这时ptrList[0]还是指向那个A的对象。
另一个例子,如果你把ptr1向ptrList1和ptrList2分别push_back,会发现ptr1.use_
count为3,如果删掉ptr1,这时查看ptrList1[0]和ptrList2[0],会发现他们都是2.这
两个数组里面存储着新的不同的智能指针对象,但是是管理同一个对象A。这两个智能
指针对象是在push_back的时候构造的,但是A的内容没有复制。
你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面
class A;//必须对称,A.h里面要有class B;
class B{定义,不要实现};
.cpp include 两个.h,实现。定义和实现分开即可。

【在 y**b 的大作中提到】
: 这个自然,就是不明白编译器怎么判断ptr1还在vector里面呢,总之reset有点特别,
: 或者智能指针实现有点特别。
: 如果是个内置指针,放入容器,然后delete该指针,会调用destructor,这个与上面的
: 行为不一样。代码如下:
: int main(int argc, char** argv)
: {
: A *ptr1 =new A();
: A *ptr2 = new A();
: std::vector ptrList;
: {

avatar
y*b
29
实际代码就是这样的,只是B的dtor放在.h里面,所以有一楼的提示,但是编译没有问
题。
我试着把B的dtor移到B.cpp里面,反而无法编译:
error: definition of implicitly-declared B::~B()’
实际代码里面B是个抽象类,但是其派生类也有一楼提示。

【在 p***o 的大作中提到】
: 前面说了,做个B.cpp,里面include A.h, B.h,再把B的dtor放到里面就好。
avatar
y*b
30
也就是说与reset无关,reset不调用dtor?
实际上reset已经让count减到0,vector clear也无法再减,但是判断是0之后调用dtor?

【在 p***o 的大作中提到】
: scope 3你clear了vector,里面的shared_ptr析构,两个count都减到0,
: 所以会调A的dtor。

avatar
l*s
31
写java的太幸福了
avatar
y*b
32
对比两个代码和输出:
比较奇怪的就是智能指针reset不调用dtor,
内置指针delete调用dtor。
行为上不一样。或者说智能指针怎么调用dtor用户根本就不应该知道?

【在 b***i 的大作中提到】
: 我觉得, 你前面ptr1是一个智能指针对象实例,然后构造一个A的实例,让ptr1来管理
: ,那么A现在的计数为1。当你把 ptr1通过push_back试图放到ptrList里面时,ptrList
: [0]出现一个智能指针的实例,把A实例的计数加到2.
: 等你删掉ptr1时,虽然ptr1.use_count是0,那是因为你把它删掉了,所以它不知道指
: 向哪个对象了。这时ptrList[0]还是指向那个A的对象。
: 另一个例子,如果你把ptr1向ptrList1和ptrList2分别push_back,会发现ptr1.use_
: count为3,如果删掉ptr1,这时查看ptrList1[0]和ptrList2[0],会发现他们都是2.这
: 两个数组里面存储着新的不同的智能指针对象,但是是管理同一个对象A。这两个智能
: 指针对象是在push_back的时候构造的,但是A的内容没有复制。
: 你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面

avatar
y*b
33
实际代码就是这样的:.cpp包含两个.h。
但是class B{}里面既有定义,也有一些实现,这个有关系吗。主要是B.h里面有多个
辅助类,还有一些继承类,有点大,不然可以快速改写一下进行测试。
还有,将B的dtor从B.h移到B.cpp里面,反而出现编译错误:
error: definition of implicitly-declared B::~B()

你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面
class A;//必须对称,A.h里面要有class B;
class B{定义,不要实现};
.cpp include 两个.h,实现。定义和实现分开即可。

【在 b***i 的大作中提到】
: 我觉得, 你前面ptr1是一个智能指针对象实例,然后构造一个A的实例,让ptr1来管理
: ,那么A现在的计数为1。当你把 ptr1通过push_back试图放到ptrList里面时,ptrList
: [0]出现一个智能指针的实例,把A实例的计数加到2.
: 等你删掉ptr1时,虽然ptr1.use_count是0,那是因为你把它删掉了,所以它不知道指
: 向哪个对象了。这时ptrList[0]还是指向那个A的对象。
: 另一个例子,如果你把ptr1向ptrList1和ptrList2分别push_back,会发现ptr1.use_
: count为3,如果删掉ptr1,这时查看ptrList1[0]和ptrList2[0],会发现他们都是2.这
: 两个数组里面存储着新的不同的智能指针对象,但是是管理同一个对象A。这两个智能
: 指针对象是在push_back的时候构造的,但是A的内容没有复制。
: 你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面

avatar
b*i
34
行为一样。你把智能指针复制到了vector里面增加了A的计数。如果你把ptrList.push_
back(ptr2)注释掉,就发现ptr2.reset()也同样调用dtor.

【在 y**b 的大作中提到】
: 对比两个代码和输出:
: 比较奇怪的就是智能指针reset不调用dtor,
: 内置指针delete调用dtor。
: 行为上不一样。或者说智能指针怎么调用dtor用户根本就不应该知道?

avatar
y*b
35
想起很久以前一点经历:有个多节点并行计算里面映射工具是用java写的,有个底层胶
水工具能把映射两端不同的语言(或模型)粘合起来,比如c、fortran、java、python
等,但是这个java工具非常慢,成为整个并行计算的瓶颈,后来我改写成多线程并行提
速十几倍(java不能跨节点并行如mpi,也不知现在还是不是),还是远远不够。再后来
找到一个类似的fortran写的映射工具,性能提升千倍。

【在 l*********s 的大作中提到】
: 写java的太幸福了
avatar
b*i
36
把所有B的代码实现全部放到cpp里面,B.h里面声明所有的函数,不要函数体。同时,B
.h里面必须class A;然后完全声明B, A.h里面必须class B;然后在定义A(没有函数体)
C++把实现放在cpp里面。我以前以为可以都放在.h里面,像java那样,看来在你这种情
况就不行,必须分开。

【在 y**b 的大作中提到】
: 实际代码就是这样的:.cpp包含两个.h。
: 但是class B{}里面既有定义,也有一些实现,这个有关系吗。主要是B.h里面有多个
: 辅助类,还有一些继承类,有点大,不然可以快速改写一下进行测试。
: 还有,将B的dtor从B.h移到B.cpp里面,反而出现编译错误:
: error: definition of implicitly-declared B::~B()
:
: 你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面
: class A;//必须对称,A.h里面要有class B;
: class B{定义,不要实现};
: .cpp include 两个.h,实现。定义和实现分开即可。

avatar
y*b
37
好的,确认这点。

push_

【在 b***i 的大作中提到】
: 行为一样。你把智能指针复制到了vector里面增加了A的计数。如果你把ptrList.push_
: back(ptr2)注释掉,就发现ptr2.reset()也同样调用dtor.

avatar
p*o
38
你不会是忘了在B.h里声明~B()吧?

【在 y**b 的大作中提到】
: 实际代码就是这样的:.cpp包含两个.h。
: 但是class B{}里面既有定义,也有一些实现,这个有关系吗。主要是B.h里面有多个
: 辅助类,还有一些继承类,有点大,不然可以快速改写一下进行测试。
: 还有,将B的dtor从B.h移到B.cpp里面,反而出现编译错误:
: error: definition of implicitly-declared B::~B()
:
: 你原来的目的,不用smart pointer,也可以通过编译,只需要在B.h里面
: class A;//必须对称,A.h里面要有class B;
: class B{定义,不要实现};
: .cpp include 两个.h,实现。定义和实现分开即可。

avatar
y*b
39
没忘。这是个基类,还得是virtual ~B()。

【在 p***o 的大作中提到】
: 你不会是忘了在B.h里声明~B()吧?
avatar
y*b
40
看来还真是这样,B的接口和实现必须完完全全绝对分离,这样编译就没有那个提示了。
道理大概是:B的接口和实现完完全全绝对分离,这样A用到B.h时不涉及B的实现,也就
不涉及到A的定义(即使B里面forward declare A),避免了循环?

,B
体)

【在 b***i 的大作中提到】
: 把所有B的代码实现全部放到cpp里面,B.h里面声明所有的函数,不要函数体。同时,B
: .h里面必须class A;然后完全声明B, A.h里面必须class B;然后在定义A(没有函数体)
: C++把实现放在cpp里面。我以前以为可以都放在.h里面,像java那样,看来在你这种情
: 况就不行,必须分开。

相关阅读
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。