@Cenlly👀 你如果愿意听详细解释,那我就先列个大纲。
首先,你先得清楚一个结论:不管C++还是Java,纠结对象具体放哪里绝大情况下都是没有意义的,并且不是语言的内容(要纠结的时候肯定语言和实现相关)。要关心的重点是怎么创建使用对象会影响哪些行为,而不是对象放哪。
第二,C++和Java的new语法都是从传统面向对象语言(Smalltalk)里抄过来的,本身其实不计较放哪。但是C++还有不用new创建对象的语法,机制上确实不一样。具体哪里不一样之后说。
第三,搞清对象模型的重点。C++跟C一样,对象是指特定类型的存储,而不是Java和class-based OO所谓的类的实例,虽然C++中类类型的前者刚好能实现后者。就存储意义上,对象实现上的生存期必须是有始有终的(至少内存不能稀里糊涂放弃了),但是Java这样要求用GC的语言本质上只允许确定性的(deterministic)创建,而对象什么时候销毁是GC而不是写代码的说了算。
C++相反,创建和销毁都是确定性的。这样做有个直接的好处:销毁时可以附带管理要求确定释放的保守资源(比如锁、数据库连接),而不仅仅是非确定性的内存。
Java是不提供这种精确的控制的,finalizer调用时机本质上是不确定的(而且主流GC为了追求吞吐量,原理上就不可能允许完全确定的资源释放),这不适合内存以外的几乎所有资源,于是你基本上显式清理。
注意没有GC不代表你必须手动显式地管理资源,C++用RAII直接可以适用所有这些不管资源是不是内存的情况。虽然Java后来有try with resource这种机制,也只能用在在局部;而C++ RAII对象指称或者包装的资源是可以直接返回值跨函数传递的。这不仅仅使代码干净容易维护(少了new废话也少),而且一定程度上更高效。退一步讲,即使要用new,正常的代码也不应该直接用,因为没对应释放就直接漏了没有GC垫着,这种情况也应该用智能指针。所以C++是提倡能不用new就尽量不用。
第四,关于传参……这是个大坑。Java那种传参本质上还是传值,虽然名义上传的这个值可以是原始类型的值或者引用值。后者按C++的做法比较接近传共享引用的智能指针,而不是C++意义上的引用——C++你可以直接传非const引用实现swap,Java就不行。其实不管语言设计还是实现中这种默认隐含别名(对象的存储允许被多个变量引用)都是比较复杂的东西,只不过Java掩盖了大部分问题让它表面上简单罢了。