我理解指针与引用的语法和一般语义,但是我应该如何决定何时在API中使用引用或指针?或多或少?
当然,有些情况需要一个或另一个(operator++需要一个引用参数),但总的来说,我发现我更喜欢使用指针(和const指针),因为语法清楚地表明变量是破坏性地传递的.
例如,在以下代码中:
void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
int a = 0;
add_one(a); // Not clear that a may be modified
add_one(&a); // 'a' is clearly being passed destructively
}
Run Code Online (Sandbox Code Playgroud)
使用指针,它总是(更加)明显地发生了什么,所以对于API等,清晰度是一个大问题,指针不是比引用更合适?这是否意味着只应在必要时使用引用(例如operator++)?是否有任何性能问题?
编辑(已完成):
除了允许NULL值和处理原始数组之外,似乎选择归结为个人偏好.我已经接受了下面引用Google的C++样式指南的答案,因为它们提出了"引用可能令人困惑,因为它们具有值语法但指针语义"的观点.
由于清理不应该为NULL的指针参数所需的额外工作(例如,add_one(0)将调用指针版本并在运行时中断),从可维护性的角度来看,使用必须存在对象的引用是有意义的,尽管这是一种耻辱失去句法的清晰度.
如果我声明一个包装在共享指针中的对象:
std::shared_ptr<myClass> myClassObject(new myClass());
Run Code Online (Sandbox Code Playgroud)
然后我想将它作为参数传递给方法:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
Run Code Online (Sandbox Code Playgroud)
以上只是增加了shared_pt的引用计数,一切都很酷吗?还是留下一个悬垂的指针?
你还是应该这样做吗?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Run Code Online (Sandbox Code Playgroud)
我认为第二种方式可能更有效,因为它只需要复制1个地址(而不是整个智能指针),但第一种方式似乎更具可读性,我不期望推动性能限制.我只是想确保没有危险的东西.
谢谢.
我在整个应用程序中广泛使用std :: tr1 :: shared_ptr.这包括在函数参数中传递对象.考虑以下:
class Dataset {...}
void f( shared_ptr< Dataset const > pds ) {...}
void g( shared_ptr< Dataset const > pds ) {...}
...
Run Code Online (Sandbox Code Playgroud)
虽然通过shared_ptr传递数据集对象可以保证它在f和g中的存在,但是这些函数可能会被调用数百万次,这会导致很多shared_ptr对象被创建和销毁.这是最近一次运行的平坦gprof配置文件的片段:
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 9.74 295.39 35.12 2451177304 0.00 0.00 std::tr1::__shared_count::__shared_count(std::tr1::__shared_count const&) 8.03 324.34 28.95 2451252116 0.00 0.00 std::tr1::__shared_count::~__shared_count()
因此,大约17%的运行时用于使用shared_ptr对象进行引用计数.这是正常的吗?
我的应用程序的很大一部分是单线程的,我正在考虑重写一些函数
void f( const Dataset& ds ) {...}
Run Code Online (Sandbox Code Playgroud)
并替换电话
shared_ptr< Dataset > pds( new Dataset(...) );
f( pds …Run Code Online (Sandbox Code Playgroud) 我记得在某处读到使用对智能指针的引用会导致内存损坏.这只是因为智能指针被销毁后使用了它的引用?或者引用计数搞砸了?
谢谢你的澄清
是否可能,如果是这样,我如何在Qt中创建一个信号/槽,它是对shared_ptr的const引用?我想要一个看起来像这样的信号:
void signal( shared_ptr<SomeClass> const & )
我知道如何在没有常量引用的情况下执行此操作,这只是类型,shared_ptr<SomeClass>但为了效率*原因我想避免复制.引用类型的相同语法不起作用:
Q_DECLARE_METATYPE(shared_ptr<SomeClass> const &)
qRegisterMetaType<shared_ptr<SomeClass> const&>();
Run Code Online (Sandbox Code Playgroud)
许多标准API都有,QString const &所以我认为它基本上是可行的,我只是无法弄清楚语法.
**性能的最大问题不是复制时间,而是当对象被复制到每个接收器时互斥锁定/解锁的数量 - 有很多.当多个线程使用该对象时,这会引起明显的减速/瓶颈.如果shared_ptr实际上只使用原子操作,这个成本也是微不足道的,那么关于信号中const引用的一般问题仍然存在.*
关于这个主题已经有几个问题,但我仍然不确定该怎么做:我们的代码库shared_ptr在许多地方使用.我必须承认,在编写时我们没有明确定义所有权.
我们有一些方法
void doSomething(shared_ptr<MyClass> ptr)
{
//doSomething() is a member function of a class, but usually won't store the ptr
ptr->foo();
...
}
Run Code Online (Sandbox Code Playgroud)
在发现了第一个(间接的)循环依赖之后,我想纠正我们设计中的错误.但我不确定如何.从上面改变方法有什么好处
void doSomething(weak_ptr<MyClass> ptr)
{
shared_ptr<MyClass> ptrShared = ptr.lock();
ptrShared->foo();
...
}
Run Code Online (Sandbox Code Playgroud)
?
我也很困惑,因为有些人说(包括谷歌风格指南),首先要确保所有权的正确性(这可能意味着引入许多weak_ptrs,例如在上述方法的例子中,但对许多成员变量也是如此)我们有).其他人说(请参阅下面的链接)你应该使用weak_ptr来打破循环依赖.但是,检测它们并不总是很容易,所以我想我是否真的应该使用shared_ptr,直到我遇到问题(并实现它们),然后修复它们?
谢谢你的想法!
也可以看看
我想通过智能指针引用将对象传递给函数.该函数可能会更改引用对象的值,但可能不会更改引用本身.有两种明显的方法可以解决这个问题.
第一种通过值传递shared_ptr的方法 - 它是引用,因此本身不需要通过引用传递.这个问题的明显问题是复制引用,这表明一些引用计数开销.
void foo (shared_ptr<bar> p)
Run Code Online (Sandbox Code Playgroud)
第二种方法是通过const引用传递shared_ptr - 避免复制shared_ptr实例,而是暗示对引用对象的访问需要两层解除引用而不是一层.
void foo (const shared_ptr<bar> &p)
Run Code Online (Sandbox Code Playgroud)
在实践中,这些理论上的开销通常是微不足道的,无关紧要的.这告诉我,我不应该为每个案例选择一种方法,而应该遵循一些标准惯例.这导致了这个问题......
我通常应该选择哪种方法的标准惯例?如果是这样,这是传统的选择吗?
编辑 - 可能值得一提 - 考虑const-by-const-reference案例的一个原因是因为有一个预先存在的约定,大多数类/结构实例是由const-reference而不是value传递的,并且shared_ptr是一个类.当然它不是重量级的(复制成本很小),因此旧约定背后的原因可能不适用.
我有一个使用boost::shared_ptrs 的程序,特别是依赖于use_count执行优化的准确性.
例如,想象一个带有两个参数指针的加法运算,称为lhs和rhs.说他们都有类型shared_ptr<Node>.当需要执行添加时,我将检查use_count,如果我发现其中一个参数的引用计数恰好为1,那么我将重用它来执行操作.如果两个参数都不能重用,我必须分配一个新的数据缓冲区并执行不合适的操作.我正在处理庞大的数据结构,因此就地优化非常有用.
因此,我无法shared_ptr无理由地复制s,即每个函数都shared_ptr通过引用或const引用来获取s以避免失真use_count.
我的问题是这样的:我有时会有一个shared_ptr<T> &我想投的东西shared_ptr<T const> &,但是如何在不扭曲使用次数的情况下做到这一点? static_pointer_cast返回一个新对象而不是引用.我倾向于认为只投下整个就行了shared_ptr,如:
void f(shared_ptr<T> & x)
{
shared_ptr<T const> & x_ = *reinterpret_cast<shared_ptr<T const> *>(&x);
}
Run Code Online (Sandbox Code Playgroud)
我非常怀疑这符合标准,但正如我所说,它可能会奏效.有没有办法做到这一点,保证安全和正确?
更新以集中问题
批评设计无助于回答这篇文章.有两个有趣的问题需要考虑:
是否有任何保证(由作者boost::shared_ptr或标准,在案例中std::tr1::shared_ptr)shared_ptr<T>并且shared_ptr<T const>具有相同的布局和行为?
如果(1)为真,那么上面是reinterpret_cast的合法使用吗?我认为你很难找到一个为上面的例子生成失败代码的编译器,但这并不意味着它是合法的.无论您的答案是什么,您能否在C++标准中找到对它的支持?
我目前正在使用C++/CLI包装一个C++类,用于.NET互操作性,遵循在托管类中保存本机指针的标准过程.在一个实例中,我有一个本机类,其功能如下:
std::shared_ptr<BaseChannel> channelData(const int RunNumber);
Run Code Online (Sandbox Code Playgroud)
我已经开始为它创建一个包装类BaseChannel.但是,如果我将原始指针传递给托管类的构造函数,则无法保证托管类指向的对象的生命周期.即shared_ptr可能超出范围,对象将被删除,托管类将保留一个悬空指针.
这种情况的常见解决方案是什么?
UPDATE
@Ben:所以我在上面这个问题中包含了保存方法的类(假设它是在一个被调用的本机类中Node,它被包装在一个名为NodeRef的托管类中:
ChannelUser^ NodeRef::ChannelData(int runNumber)
{
// mpNode is native class pointer of type Node held in managed class
// wrapper called NodeRef
std::shared_ptr<BaseChannel> spBaseChannel = mpNode->channelData(runNumber);
// ChannelUser is using clr_scoped_ptr to hold the shared_ptr
ChannelUser^ channelUser = gcnew ChannelUser(spBaseChannel);
return channelUser;
}
Run Code Online (Sandbox Code Playgroud)
因为shared_ptr没有增加引用计数,因为它通过引用传递给托管类,这是否意味着
只要此shared_ptr在范围内,它指向的对象仍然存在,因为它的引用计数至少为1
c++ ×8
shared-ptr ×5
boost ×2
c++11 ×2
.net ×1
c++-cli ×1
c++-faq ×1
casting ×1
mixed-mode ×1
performance ×1
pointers ×1
qt ×1
reference ×1
wrapper ×1