我为链接列表写了以下课程。有两个单独的函数可用于添加新节点(from pyspark import SparkConf, SparkContext
from pyspark.sql import SQLContext, Window
import pyspark.sql.functions as psf
config = SparkConf().setMaster('local')
spark = SparkContext.getOrCreate(conf=config)
sqlContext = SQLContext(spark)
spark_df = sqlContext.read.csv('transactions.csv', header=True)
window = Window.partitionBy('client_id').orderBy('start').rowsBetween(Window.unboundedPreceding, 0)
@psf.udf('string')
def get_number_of_transactions(curr_date):
return spark_df[spark_df['end'] < curr_date].count()
spark_df \
.withColumn('number_of_past_transactions',
psf.row_number().over(window) - 1) \
.withColumn('total_amount', psf.sum(psf.col('amount')).over(window)) \
.withColumn('closed_transactions',
get_number_of_transactions(psf.col('end'))) \
.show()
和addValue1
)。一个使用指向节点的指针,另一个不使用。
addValue2
推荐这两个中的哪个?为什么?两者之间有什么区别吗?如果我们不使用new运算符,那么以后就不需要使用delete运算符。对我来说,这似乎是一个优势。真的吗?
答案 0 :(得分:6)
addValue2
将给您造成严重破坏,因为您要存储指向节点newNode
的指针,该节点具有自动存储期限。一旦超出范围,指针&newNode
将悬挂。
轰!
请注意,如果这正是编译器为您执行的工作,则显式地写出析构函数也是不合时宜的。如果您需要引入析构函数以使其virtual
,请编写
virtual ~linkedList() = default;
在您的情况下,尽管您需要delete
分配了new
的节点,否则您的类会泄漏内存。
最后,如果您根本不想担心内存,请使用
typedef linkedList std::list;
然后去酒吧
答案 1 :(得分:3)
两者是非常不同的。如果有人改写addNode2
来使用手动分配而不是自动分配,则它看起来像这样(原始代码作为注释):
void addValue2(int n) {
Node* newNode = Node(); // Node newNode;
newNode->data = n; // newNode.data = n;
newNode->next = head; // newNode.next = head;
this->head = newNode; // this->head = &newNode;
delete newNode; // (newNode is deleted automatically !)
}
使用自动存储时,对象超出范围会立即被销毁。单独使用该方法并没有太大危害,但是它使this->head
成为无效的指针(方法返回时,指向对象newNode
的指针不再存在)。取消引用无效的指针是无法定义的行为,因此在使用该指针的所有其他方法中都可能发生任何事情(包括您所期望的,实际上是未定义行为的最坏体现,因为您不会发现它,直到为时已晚)。 / p>
更符合原始代码,但经过简化,出于相同的原因,这是错误的:
int* return_invalid_pointer() {
int x = 3;
return &x;
} // <--- x's lifetime ends here
此处x
的生命周期绑定到函数的主体。您可以返回一个指向局部变量的指针,但是该指针在函数外部没有任何意义,因为它指向的值不再存在。