我来自Java,已经开始用C++处理对象。但我想到的一件事是,人们经常使用指向对象的指针,而不是对象本身,例如这个声明:
Object *myObject = new Object;
而不是:
Object myObject;
或者不使用函数,我们假设testfunc()
,如下所示:
myObject.testFunc();
我们必须写:
myObject->testFunc();
但我不明白为什么我们要这样做。我认为这与效率和速度有关,因为我们可以直接访问内存地址。我说的对吗?
这个问题有很多很好的答案,包括前向声明,多态性等重要的用例,但我觉得您的问题的“灵魂”的一部分没有得到回答--即Java和C++的不同语法意味着什么。
让我们考察一下比较这两种语言的情况:
Object object1 = new Object(); //A new object is allocated by Java
Object object2 = new Object(); //Another new object is allocated by Java
object1 = object2;
//object1 now points to the object originally allocated for object2
//The object originally allocated for object1 is now "dead" - nothing points to it, so it
//will be reclaimed by the Garbage Collector.
//If either object1 or object2 is changed, the change will be reflected to the other
与此最接近的等价物是:
Object * object1 = new Object(); //A new object is allocated on the heap
Object * object2 = new Object(); //Another new object is allocated on the heap
delete object1;
//Since C++ does not have a garbage collector, if we don't do that, the next line would
//cause a "memory leak", i.e. a piece of claimed memory that the app cannot use
//and that we have no way to reclaim...
object1 = object2; //Same as Java, object1 points to object2.
让我们看看另一种C++方式:
Object object1; //A new object is allocated on the STACK
Object object2; //Another new object is allocated on the STACK
object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1,
//using the "copy assignment operator", the definition of operator =.
//But, the two objects are still different. Change one, the other remains unchanged.
//Also, the objects get automatically destroyed once the function returns...
最好的想法是--或多或少--Java(隐式地)处理指向对象的指针,而C++可能处理指向对象的指针,也可能处理对象本身。这种情况也有例外--例如,如果您声明Java“原始”类型,它们是被复制的实际值,而不是指针。所以,
int object1; //An integer is allocated on the stack.
int object2; //Another integer is allocated on the stack.
object1 = object2; //The value of object2 is copied to object1.
也就是说,使用指针不一定是正确或错误的处理方式;然而,其他答案已经令人满意地涵盖了这一点。不过,一般的想法是,在C++中,您可以更多地控制对象的生存期,以及它们的生存位置。
回到原点--object*object=newobject()
构造实际上最接近典型的Java(或C#)语义。
指针有很多用例。
多态行为。对于多态类型,使用指针(或引用)来避免切片:
class Base { ... };
class Derived : public Base { ... };
void fun(Base b) { ... }
void gun(Base* b) { ... }
void hun(Base& b) { ... }
Derived d;
fun(d); // oops, all Derived parts silently "sliced" off
gun(&d); // OK, a Derived object IS-A Base object
hun(d); // also OK, reference also doesn't slice
引用语义和避免复制。对于非多态类型,指针(或引用)将避免复制潜在的昂贵对象
Base b;
fun(b); // copies b, potentially expensive
gun(&b); // takes a pointer to b, no copying
hun(b); // regular syntax, behaves as a pointer
请注意,C++11具有move语义,可以避免将大量昂贵对象复制到函数参数和返回值中。但是使用指针肯定会避免这些,并且允许在同一个对象上使用多个指针(而一个对象只能移动一次)。
资源获取。使用new
运算符创建指向资源的指针是现代C++中的一种反模式。使用特殊的资源类(标准容器之一)或智能指针(std::unique_ptr<>
或std::shared_ptr<>
)。考虑:
{
auto b = new Base;
... // oops, if an exception is thrown, destructor not called!
delete b;
}
vs。
{
auto b = std::make_unique<Base>();
... // OK, now exception safe
}
原始指针只应用作“视图”,而不应以任何方式涉及所有权,无论是通过直接创建还是隐式地通过返回值。请参见C++常见问题解答中的问答。
更细粒度的生命期控制每次复制共享指针时(例如,作为函数参数),它所指向的资源将保持活动状态。常规对象(不是由new
创建的,或者由您直接创建,或者在资源类中创建)在超出作用域时被销毁。
非常不幸的是,你经常看到动态分配。这正好说明有多少糟糕的C++程序员。
从某种意义上说,你有两个问题被捆绑成一个。第一个是什么时候应该使用动态分配(使用new
)?二是什么时候应该使用指针?
重要的带回家信息是,你应该始终使用适当的工具为工作。在几乎所有情况下,都有比执行手动动态分配和/或使用原始指针更合适和更安全的东西。
在您的问题中,您已经演示了创建对象的两种方法。主要区别是对象的存储持续时间。当在块内执行对象MyObject;
时,对象的创建具有自动存储持续时间,这意味着当它超出作用域时将自动销毁。当您执行New Object()
时,对象具有动态存储持续时间,这意味着它将保持活动状态,直到您显式地删除
它。您应该只在需要时使用动态存储持续时间。也就是说,在可能的情况下,您应该始终倾向于创建具有自动存储持续时间的对象。
可能需要动态分配的两种主要情况:
当您确实需要动态分配时,应该将其封装在智能指针或其他执行RAII的类型中(如标准容器)。智能指针提供动态分配对象的所有权语义。例如,看看std::unique_ptr
和std::shared_ptr
。如果适当地使用它们,几乎可以完全避免执行自己的内存管理(参见零规则)。
但是,除了动态分配之外,原始指针还有其他更通用的用途,但大多数都有您应该喜欢的替代方法。就像以前一样,除非你真的需要指针,否则总是要选择其他选项。
>
您需要引用语义。有时,您希望使用指针传递对象(不管它是如何分配的),因为您希望传递对象的函数能够访问该特定对象(而不是该对象的副本)。然而,在大多数情况下,您应该更喜欢引用类型而不是指针,因为这正是它们的设计目标。注这不一定是关于将对象的生存期延长到当前作用域之外,如上面的情形1所示。和前面一样,如果您可以传递对象的副本,那么就不需要引用语义。
你需要多态性。您只能通过对象的指针或引用多态地(即根据对象的动态类型)调用函数。如果这是您需要的行为,那么您需要使用指针或引用。同样,应优先考虑参考文献。
您希望通过允许在省略对象时传递nullptr
来表示对象是可选的。如果它是一个参数,您应该更喜欢使用默认参数或函数重载。否则,您最好使用封装此行为的类型,例如std::optional
(在C++17中引入-对于早期的C++标准,使用boost::optional
)。
您希望解耦编译单元以改善编译时间。指针的有用属性是,您只需要指向类型的前向声明(要实际使用对象,您需要一个定义)。这允许您对编译过程的部分进行解耦,这可能会显著改善编译时间。参见Pimpl成语。
您需要与C库或C样式库进行接口。此时,您被迫使用原始指针。你能做的最好的事情就是确保你只在最后可能的时刻让你的原始指针松动。您可以从智能指针中获取原始指针,例如,通过使用其get
成员函数。如果一个库为您执行了一些它希望您通过句柄释放的分配,您通常可以将句柄包装在一个智能指针中,并使用一个自定义删除器来适当地释放对象。
问题内容: 这个来自json.Unmarshal docs的示例(为便于使用而不是进行了稍微修改)有效,没有错误: 工作示例的游乐场链接 但是这个经过稍微修改的示例却没有: 非工作示例的游乐场链接 它显示了这个实际上并没有帮助的模糊错误(看起来更像是一个函数调用,而不是错误的IMO): json:Unmarshal(nil * main.Animal) 这似乎是因为它是未初始化的指针。但是文档说(
问题内容: 我不确定为什么列出项目时为什么需要使用ul-li而不是简单地使用div。我可以使两者看起来完全一样,因此与创建div相比,创建无序列表的功能优势在哪里? 问题答案: 为了语义正确。HTML具有表达事物列表的功能,它可以帮助Google机器人,屏幕阅读器以及所有不仅仅关心网站外观的用户更好地了解您的内容。
问题内容: 我正在一个JavaScript项目上,只是想知道为什么对象实例不继承和其他方法,而不必调用超类(superobject?)方法。 我看过了MDN文档,实际上有“非标准”属性方法。 但这些已被弃用。为什么要转向方法? 在我看来,类似的东西比更好。对于其他一些Object方法,我也会说同样的话。 问题答案: 这是为了避免发生冲突-通常情况下,对象的问题不具有所需值的属性。 JS中的对象通常
问题内容: 我是一个相对较新的QA工程师,致力于学习Selenium(使用Java),并且我想使用页面对象为页面建模。 目前,我的页面对象类是静态变量(用于定位页面元素的对象)和静态方法(用于获取By对象并执行页面功能)的集合。这对我来说似乎是最简单的方法,因为我的方法不需要依赖任何实例变量,而只需依赖定位符。 我只是在测试代码中根据需要调用这些方法。 但是,我读到的有关页面对象的所有内容都涉及实
问题内容: 最近,在使用C#时,我意识到我可以从的静态函数甚至其他Foo对象调用对象的私有函数。在我了解了访问修饰符的所有内容之后,这对我来说听起来很奇怪。 据我所知,当函数执行某种内部过程的一部分时,会将其私有。只有对象本身知道何时使用那些功能,因为其他对象不应该/不能控制对象的流程。是否有任何理由为什么应该从这个非常简单的规则中排除同一类的其他对象? 根据要求,举一个例子: 问题答案: 当你将
问题内容: 目前使用jQuery,当我需要在发生点击时做一些事情时,我会像这样… 我正在看别人在项目上有的代码,他们这样做是… 请注意,就我所知,它似乎在做相同的事情,除了它们使用的是live()函数(现在已弃用并且jQuery文档说要使用live()函数),但是无论哪种方式,为什么要使用live / on()而不是我的第一个示例? 问题答案: 因为您可能具有动态生成的元素(例如,来自AJAX调用
问题内容: 在selenium.webdriver.common.by中使用By代替常规的find_element_by _…方法的目的和好处是什么?例如: vs: 问题答案: 据documentatio ñ 似乎是一种“ 私人 所使用的”法的方法和还可以使用页面对象 因此,使用Page Object模式是您可能需要+ 而不是的原因。 例如,您有一些包含元素值的变量 然后用它来定位元素为 如果由于
问题内容: 我习惯于进行Java编程,在编程时,您无需真正考虑指针。但是,此刻我正在用C ++编写程序。在创建具有其他类成员的类时,何时应该使用指针,何时不应该使用指针?例如,什么时候我想这样做: 与此相反: 问题答案: 首先避免指针。 在以下情况下使用它们: 您想使用Pimpl习惯用法或抽象工厂。 该实例实际上是由程序的其他部分管理的,而该类仅需要能够访问它。 您想推迟对象的构建(即,您想 在