【问题标题】:Completely destroy all traces of an object in Perl在 Perl 中彻底销毁一个对象的所有痕迹
【发布时间】:2015-08-05 19:43:53
【问题描述】:

我正在编写一个从不断变化的外部源获取数据的 Perl 脚本。当从这个外部源添加或删除数据时,我希望我的 Perl 脚本中的数据结构能够反映这些更改。我可以通过创建以类似方式存储数据的对象并在尝试访问我的 Perl 脚本中的数据时同步这些对象中的数据来做到这一点。

这很好用,并且可以获取添加到此外部源的任何新数据;但是,当删除任何数据时会出现问题。如果删除了任何数据,我希望我的 Perl 脚本中对它的所有现有引用也被销毁,这样用户就不能再尝试访问数据而不会引发错误。


我能想到的唯一方法是在确定数据不再存在后立即取消定义对数据的内部引用。然而,它似乎;当引用未定义时,它不会删除存储在引用位置的数据,它只会删除保存引用的变量中的数据。


这是一个演示我的问题的测试脚本:

#!/usr/bin/perl

# Always use these
use strict;
use warnings;

####################################################################################################
# Package to create an object
package Object;

use Moose;

# Define attributes
has 'name' => (is => 'ro', isa => 'Str', required => 1);

####################################################################################################
# Package to test with
package test;

# Create an object
my $object1 = Object->new('name' => 'Test Object');
print 'OBJECT1 NAME: '.$object1->name()."\n";

# Create another reference to the object
my $object2 = $object1;

# Print the name
print 'OBJECT2 NAME: '.$object2->name()."\n";

# Print both references
print "\n";
print "OBJ  : $object1\n";
print "OBJ2 : $object2\n";
print "\n";

# Undefine the reference to object2
undef $object2;

# Try to print both names
print 'OBJECT1 NAME: '.$object1->name()."\n";
print 'OBJECT2 NAME: '.$object2->name()."\n";

输出:


我怎样才能完全销毁一个对象的所有痕迹,以便任何访问它的数据的尝试都会导致错误?


编辑:

这是一个不同的例子,可以更好地解释我想要实现的目标。

假设我有一个文件对象:

my $file = File->new();

现在我想获取该文件中的文本

my $text = $file->text();

现在我又得到了它(出于某种未知原因)

my $text2 = $file->text();

我希望能够修改$text 并让它直接影响$text2 的内容以及更改文件的实际text 属性。

我基本上是在尝试将变量联系在一起,所以如果一个变量发生变化,它们都会发生变化。此外,如果一个被删除,它们将全部被删除。

这也意味着如果 text 属性发生更改,$text1$text2 也会随之更改以反映新值。

这可以使用某种别名来完成吗?

【问题讨论】:

  • 所以你想让$object1在你输入undef $object2之后变成undef
  • 是的,这就是我要找的。​​span>
  • 是的,我首先看了一下,它告诉您如何执行 undef,但不是如何摆脱它的每个实例。
  • 您是否真的需要从内存中物理擦除对象,或者您是否可以简单地向对象添加像is_deleted 这样的字段?这样每次您尝试访问该对象时,您都可以检查它是否仍然有效。

标签: perl oop reference destroy


【解决方案1】:

Perl 使用引用计数来“淘汰”数据。

你的程序在做什么如下:

  1. 创建一个对象并分配一个对$object1的引用
  2. 将该引用复制到$object2 并增加对象的引用计数
  3. 更改$object2 的值并减少对象的引用计数。

最后,您仍然通过$object1 持有该对象,并且当您持有该对象时,它的引用计数不会降至 1 以下。从技术上讲,当您的程序关闭时,$object1 变量将在超出范围时被销毁,此时对象的引用计数将降至 0,perl 将查找并尝试调用它的 DESTROY 方法。

如果您真的想“看到”被销毁的项目,您可能需要考虑定义一个DESTROY 方法,该方法会在对象销毁时打印出一条消息。这样你就可以“放开”你的最后一个参考,仍然可以看到它的破坏。

【讨论】:

  • 好的,这解释了正在发生的事情,但是我怎样才能强制 Perl 同时消除 $object1$object2
  • @tjwrona1992 远距离行动通常不被认为是可取的。但不是复制你的参考,我想你可以创建$object2作为$object1别名
  • 我相信别名是我正在寻找的。看起来这可以使用typeglob (perlmonks.org/?node_id=976523) 来完成。我还没有完全了解typeglobs 以及它们是如何工作的。
  • @tjwrona1992 不推荐使用 typeglob。它们依赖于全局/包变量。 (我们又回到了远距离行动的想法。)向 CPAN 寻求词汇解决方案。
【解决方案2】:

您不能释放仍在使用的对象。让对象本身跟踪它是否仍然有效。

sub new { 
   my ($class, $data) = @_;
   my $self = bless({}, $class);
   $self->{valid} = 1;
   $self->{data} = $data;
   return $self;
}

sub delete {
   my $self = shift;
   undef(%$self);
}

sub data {
   my $self = shift;
   croak("Invalid object") if !$self->{valid};
   $self->{data} = $_[0] if @_;
   return $self->{data};
}

例子:

my $o1 = Class->new('big_complex_data');
my $o2 = $o1;

say $o1->data();
say $o2->data();

$o1->delete();
say $o2->name();

输出:

big_complex_data
big_complex_data
Invalid object at a.pl line 39.

【讨论】:

  • 如果这些对象只有一个属性会很好,但它们可以有许多不同的属性,其中一些属性可能仍然有效。我知道我上面的例子没有考虑到这一点,因为我删除了整个对象,但实际上我有一个非常复杂的数据结构,其中只有小部分可能变得无效,如果它们无效,我想删除它们,所以它们不能再次访问而不会导致程序崩溃。我只希望它在用户明确尝试检索无效数据时崩溃。
  • 提示:如果有帮助,delete 可以记录堆栈帧,以便将其包含在访问无效对象时抛出的错误消息中。
  • 这不是问题。您的属性构建器将构建所有属性以检查对象是否有效。
  • 对于如此大的复杂对象,只需更改一个标志就会将对象留在内存中。随着程序继续运行,这些“已删除”的对象会堆积起来,导致程序消耗更多内存,并且会像内存泄漏一样。
  • 首先,这似乎不太可能。只要您仍在使用它,它只会留在内存中。为什么你在删除对象后继续使用它们?其次,为什么你有这么大的数据结构的多个副本?您的对象都应该引用相同的数据结构。这样不仅可以节省内存,还可以完全消除这个问题。
猜你喜欢
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
  • 2014-06-24
  • 1970-01-01
  • 2012-12-06
相关资源
最近更新 更多