我很好奇,因为我开始采用更多的提升习语和看似最好的做法我想知道我的c ++甚至远远看起来像过去的c ++,经常在典型的例子和那些人的头脑中找到还没有被介绍过"Modern C++"?
我一直在评估各种智能指针实现(哇,有很多),在我看来,大多数可以分为两大类:
1)此类别对引用的对象使用继承,以便它们具有引用计数,并且通常实现up()和down()(或它们的等价物).IE,要使用智能指针,您指向的对象必须从ref实现提供的某些类继承.
2)此类别使用辅助对象来保存引用计数.例如,不是将智能指针指向对象,而是实际指向此元数据对象...谁具有引用计数以及up()和down()实现(并且通常为指针提供一种机制)获取指向的实际对象,以便智能指针可以正确实现operator - >()).
现在,1有一个缺点,它强制你想引用的所有对象计数从一个共同的祖先继承,这意味着你不能使用它来引用你无法控制源代码的计数对象至.
2有一个问题,因为计数存储在另一个对象中,如果你有一个指向现有引用计数对象的指针被转换为引用的情况,你可能有一个错误(IE,因为计数不在实际的对象,新的引用没有办法得到计数...引用ref复制构造或赋值是好的,因为它们可以共享count对象,但如果你必须从指针转换,你'完全被冲洗了)...
现在,正如我所理解的那样,boost :: shared_pointer使用了机制2,或类似的东西......那就是说,我不能完全决定哪个更糟糕!我只使用机制1,在生产代码中......有没有人有这两种风格的经验?或许还有另一种方式比这两种方式更好?
TR1引入了shared_ptr,weak_ptr,scoped_ptr和unique_ptr等.
我想知道这些类型之间的各种转换/类型提升规则.
例如,将scoped_ptr分配给shared_ptr时会发生什么?这种转换是否可行/有意义以及此类转换的一些用例是什么?
(具体是否有表格?)
让我先说一下,我所知道的是我要提出的是一个致命的罪,并且即使考虑它,我也可能会在编程地狱中燃烧.
也就是说,我仍然有兴趣知道为什么这不起作用.
情况是:我有一个我在任何地方使用的引用计数智能指针类.它目前看起来像这样(注意:不完整/简化的伪代码):
class IRefCountable
{
public:
IRefCountable() : _refCount(0) {}
virtual ~IRefCountable() {}
void Ref() {_refCount++;}
bool Unref() {return (--_refCount==0);}
private:
unsigned int _refCount;
};
class Ref
{
public:
Ref(IRefCountable * ptr, bool isObjectOnHeap) : _ptr(ptr), _isObjectOnHeap(isObjectOnHeap)
{
_ptr->Ref();
}
~Ref()
{
if ((_ptr->Unref())&&(_isObjectOnHeap)) delete _ptr;
}
private:
IRefCountable * _ptr;
bool _isObjectOnHeap;
};
Run Code Online (Sandbox Code Playgroud)
今天我注意到sizeof(Ref)= 16.但是,如果我删除布尔成员变量_isObjectOnHeap,则sizeof(Ref)减少到8.这意味着对于我的程序中的每个Ref,有7.875个浪费的RAM字节......并且我的程序中有很多很多Refs .
好吧,这似乎浪费了一些内存.但我真的需要那些额外的信息(好吧,幽默我,并为了我真正做的讨论而假设).我注意到,由于IRefCountable是一个非POD类,它(可能)总是被分配在一个字对齐的内存地址上.因此,(_ptr)的最低有效位应始终为零.
这让我想知道...有什么理由说我不能将我的一点布尔数据或指针的最低有效位,并因此将sizeof(Ref)减少一半而不牺牲任何功能?当然,在取消引用指针之前,我必须小心取出那个位,这会使指针解引用效率降低,但这可能是因为Refs现在更小,因此更多可以立即适应处理器的缓存,依此类推.
这是合理的事吗?还是我为自己创造了一个受伤的世界?如果是后者,那么伤害到底是怎么回事?(请注意,这是需要在所有合理的现代桌面环境中正确运行的代码,但它不需要在嵌入式计算机或超级计算机或任何异国情况下运行)
我正在尝试使用继承和模板参数推导std::shared_ptr.正如您在下面的示例代码中所看到的,我正在传递shared_ptr<Derived>一个模板化的非成员函数,该函数应该进行模板参数推导.如果我手动命名类型一切正常,如果我让它做模板参数推导它没有.它会看起来好像编译器无法弄清楚的类型,但该错误消息表明,它没有.我不确定这里发生了什么,我将不胜感激.(Visual Studio 2010)
#include <memory>
template <typename T>
class Base {};
class Derived : public Base<int> {};
template <typename T>
void func(std::shared_ptr<Base<T> > ptr) {};
int main(int argc, char* argv[])
{
std::shared_ptr<Base<int>> myfoo(std::shared_ptr<Derived>(new Derived)); // Compiles
func(myfoo); // Compiles
func<int>(std::shared_ptr<Derived>(new Derived)); // Compiles
func(std::shared_ptr<Derived>(new Derived)); // Doesn't compile. The error message suggests it did deduce the template argument.
return 0;
}
Run Code Online (Sandbox Code Playgroud)
错误消息:
5> error C2664: 'func' : cannot convert parameter 1 from 'std::tr1::shared_ptr<_Ty>' to …Run Code Online (Sandbox Code Playgroud) 我一直在努力防止用户使用没有智能指针的类.因此,强制它们使对象被智能指针分配和管理.为了得到这样的结果,我尝试了以下方法:
#include <memory>
class A
{
private :
~A {}
// To force use of A only with std::unique_ptr
friend std::default_delete<A>;
};
Run Code Online (Sandbox Code Playgroud)
如果您只希望您的类用户能够通过操作类的实例,那么这项工作非常好std::unique_ptr.但它不起作用std::shared_ptr.所以我想知道你是否有任何想法来获得这样的行为.我发现的唯一解决方案是执行以下操作(使用friend std::allocator_traits<A>;效率不高):
#include <memory>
class A
{
private :
~A {}
// For std::shared_ptr use with g++
friend __gnu_cxx::new_allocator<A>;
};
Run Code Online (Sandbox Code Playgroud)
但这种解决方案不可移植.也许我做错了.
std::unique_ptr::operator-> 有签名
pointer operator->() const noexcept;
Run Code Online (Sandbox Code Playgroud)
所以operator->是常量而是返回一个可变的指针.这允许代码如:
void myConstMemberFunction() const
{
myUniquePtrMember->nonConstFunction();
}
Run Code Online (Sandbox Code Playgroud)
为什么标准允许这样做,以及防止使用的最佳方法是什么?
C++ 11引入了引用计数的智能指针std::shared_ptr.作为引用计数,这些指针无法自动回收循环数据结构.但是,可以自动收集参考周期,例如Python和PHP.为了将此技术与垃圾收集区分开来,问题的其余部分将其称为循环中断.
鉴于似乎没有为C++添加等效功能的建议,是否有一个根本原因,为什么类似于已经部署在其他语言中的循环断路器不适用std::shared_ptr?
请注意,这个问题不能归结为"为什么没有GC for C++",这已经被问过了.C++ GC通常是指一个自动管理所有动态分配对象的系统,通常使用某种形式的Boehm保守收集器实现.有人指出,这样的收集器不适合RAII.由于垃圾收集器主要管理内存,甚至可能在内存不足之前就被调用,并且C++析构函数管理其他资源,依赖于GC来运行析构函数会在最坏情况下引入非确定性和资源不足.它还指出,在存在更明确和可预测的智能指针的情况下,完全不需要GC.
但是,基于库的智能指针循环断路器(类似于引用计数解释器使用的循环断路器)与通用GC有重要区别:
shared_ptr.这些对象已经参与共享所有权,因此必须处理延迟的析构函数调用,其确切的时间取决于所有权结构.std::enable_shared_from_this.不使用它的对象不必为控制块中的额外空间付费以保存循环断路器元数据.reset().这足以打破循环并自动销毁对象.要求对象提供并清除其强力引用(根据要求),确保循环断路器不会破坏封装.缺乏自动循环中断的建议表明该想法因实际或哲学原因而被拒绝.我很好奇原因是什么.为了完整起见,这里有一些可能的反对意见:
"它会引入循环shared_ptr对象的非确定性破坏." 如果程序员控制了循环断路器的调用,那么它就不是非确定性的.此外,一旦被调用,循环断路器的行为将是可预测的 - 它将破坏所有当前已知的循环.这类似于shared_ptr析构函数在引用计数降至零后如何销毁基础对象,尽管这可能会导致"非确定性"级联的进一步破坏.
"就像任何其他形式的垃圾收集一样,循环断路器会在程序执行中引入暂停." 实现此功能的运行时的经验表明,暂停是最小的,因为GC只处理循环垃圾,所有其他对象都通过引用计数回收.如果永远不会自动调用循环检测器,则循环断路器的"暂停"可能是运行它的可预测结果,类似于破坏大型std::vector运行大量析构函数的方法.(在Python中,循环gc是自动运行的,但有一些API可以在不需要它的代码段暂时禁用它.稍后重新启用GC将获取同时创建的所有循环垃圾.)
"循环断路器是不必要的,因为循环不是那么频繁,可以很容易地避免使用std::weak_ptr." 事实上,循环很容易在许多简单的数据结构中出现 - 例如,一个树,其中孩子有一个指向父项的后向指针,或一个双向链表.在某些情况下,复杂系统中的异质对象之间的循环仅偶尔与某些数据模式形成,并且难以预测和避免.在某些情况下,用弱变量替换哪个指针远非明显.
最近我从C++ 11开始.我研究过weak_ptr.有两种获取原始指针的方法.
lock() 功能
shared_ptr<Foo> spFoo = wpPtr.lock();
if(spFoo) {
spFoo->DoSomething();
}
Run Code Online (Sandbox Code Playgroud)expired() 功能
if(!wpPtr.expired())
{
shared_ptr<Foo> spFoo = wpPtr.lock();
spFoo->DoSomething();
}
Run Code Online (Sandbox Code Playgroud)哪种方式更好?这两种方式有什么不同?
我正在尝试用 Rust 解决一些 Leetcode 问题。然而,我在 LeetCode 的实现过程中遇到了一些困难TreeNode。
use std::cell::RefCell;
use std::rc::Rc;
// TreeNode data structure
#[derive(Debug, PartialEq, Eq)]
pub struct TreeNode {
pub val: i32,
pub left: Option<Rc<RefCell<TreeNode>>>,
pub right: Option<Rc<RefCell<TreeNode>>>,
}
impl TreeNode {
#[inline]
pub fn new(val: i32) -> Self {
TreeNode {
val,
left: None,
right: None,
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我想做一个中序遍历,如何解开 的TreeNode对象Option<Rc<RefCell<TreeNode>>>,访问它.val .left .right并将它们作为输入传递到递归函数中?
我努力了:
pub struct Solution;
impl Solution {
pub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {
let …Run Code Online (Sandbox Code Playgroud) smart-pointers ×10
c++ ×9
c++11 ×3
boost ×1
c++03 ×1
class-design ×1
const ×1
dereference ×1
inheritance ×1
raii ×1
rules ×1
rust ×1
shared-ptr ×1
templates ×1
weak-ptr ×1