如果定义构造函数,为什么必须使用指针?

时间:2018-03-06 19:43:32

标签: c++ class

当我为我的类创建构造函数时,C ++迫使我做两件事:

  • rect 定义为指针,因为我正在使用 new 关键字。
  • 当我将其定义为指针时,我需要使用 - > 而不是来访问其属性,方法。

我的问题是,为什么会这样?我来自Java背景,它不像Java那样,所以我想这里有一个优势,我无法看到。有人可以为我澄清一下吗?我为新手问题道歉。

使用构造函数

// classes example
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area() {return width*height;}

  Rectangle(int w,int h) {
    width = w;
    height = h;
  }
};


int main () {
  Rectangle* rect = new Rectangle(5,8);

  cout << "area: " << rect -> area();
  return 0;
}

没有构造函数

// classes example
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area() {return width*height;}
};

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}

3 个答案:

答案 0 :(得分:2)

在用于访问类成员的表示法中,C ++和Java之间存在一些相似之处:

C ++的SELECT *, min(okd.position) FROM -- all deals (SELECT * FROM "Deals" as d WHERE (d.active = true) AND (d.latitude BETWEEN 40 AND 41) AND (d.longitude BETWEEN -75 AND -70)) AS okd LEFT JOIN -- used deals (SELECT * FROM "Deals" as ad INNER JOIN (SELECT * FROM "DealsUsed" AS du WHERE du."CustomerId" = 1) AS rd ON rd."DealId" = ad.id) as dd --deals that were not used, grouped by user and sorted by position ON dd."UserId" = okd."UserId" AND dd.position = okd.position WHERE dd.id IS NULL GROUP BY okd."UserId"; 通常等同于a->b。我通常说,因为在C ++中可以重载指向成员运算符(*a).b的指针,而不能重载->(尽管C ++标准中有一些变动)委员会放松一下。)

至于类实例的创建,C ++中出现了复杂性,因为与Java不同,您基本上有两种关于类(和普通旧数据)类型实例化的选择。你可以使用

  1. 自动存储时间:.

  2. 动态存储时间:Rectangle rect(1, 2);

  3. (其他几个选择 - Rectangle* rect = new Rectangle(1, 2);static有更多的第一种口味。)

    请注意,在C ++中,与Java不同,您需要调用thread_local free 与{em>指针相关联的内存{{ 1}}。 C ++中的一些类(例如delete)在管理内存方面有很大帮助。

答案 1 :(得分:2)

您不必仅因为您的班级中有构造函数而使用指针。这两件事是无关的。

的例子
Rectangle* rect = new Rectangle(5,8);

可以很容易地在没有指针的情况下重写:

Rectangle rect(5,8);

另请注意,Rectangle rect;仍然会调用构造函数:默认构造函数(不带参数),Rectangle::Rectangle()。如果您自己没有定义构造函数,编译器将为您生成默认构造函数。

答案 2 :(得分:0)

Java和C ++之间的主要区别(无论如何重要)是内存管理。

在第一次近似中,在Java中,所有内容都是在堆上分配的对象,并且您可以通过引用访问这些对象(尽管它们更像C ++指针而不是C ++引用,有时您可能会看到它们被称为指针)。例外是int等基本类型,这就是为什么你不能创建ArrayList<int>的原因。系统使用垃圾收集管理对象的生命周期;当没有任何东西能够引用某个物体时,就会收集它。

C ++为您提供更多选择。一种选择是您可以使用new关键字在堆上分配对象并通过指针访问这些对象。到目前为止,这与Java完全相同,这可能是Java借用new关键字的原因。这里的主要区别是C ++没有跟踪这些对象的生命周期;当您希望销毁对象时,必须手动使用delete关键字。

或者,您可以在堆栈上进行分配,如下所示:

int main () {
  Rectangle rect(5,8);

  cout << "area: " << rect.area();
  return 0;
}

请注意,这确实使用了构造函数!在这种情况下,该rect对象的内存不在堆上,它在堆栈上。在某些方面,它现在更像是像int这样的原始类型;它的存在只要它的范围。有些人会使用术语&#34;值语义&#34;描述这样的事情。当它超出范围时,对象就会被破坏。

与堆相比,使用堆栈有一些优点和缺点。一个是在堆栈上分配内存比在堆上分配内存要快得多(在堆栈上分配内存几乎是通过增加一个指针来完成的!);另一个是堆栈对象的生命周期是完全明显的。缺点是堆栈具有相对少量的可用内存;如果你试图在堆栈上分配太多,那么你就有可能...... 溢出你的堆栈

在这里标记的一个关键是C ++与Java的不同之处在于,在Java中,当一个对象被销毁时,由垃圾收集器决定。从程序的角度来看,它是非确定性的。这不是C ++的情况;当对象退出作用域(对于堆栈分配的对象)或者它们被显式删除(对于堆上的对象)时,对象将被销毁。所以这就是为什么C ++有了析构函数是有道理的,因为你知道它们什么时候被调用。

这就是为什么C ++没有像Java的try-with-resources / AutoCloseable(或者就此而言,C#中的IDisposable)这样的东西;析构函数扮演了这个角色。例如,如果您使用std::ifstream从文件中读取,并在堆栈上创建该对象,则当ifstream超出范围时,将关闭该文件,这是正确的适用于任何范围;你不需要像try-with-resources这样的特殊语法。

现在,C ++中的典型做法是使用在堆栈上分配的东西(带有值语义)来提供抽象来帮助您进行内存管理。这样的示例包括std :: vector&lt;&gt;和std :: unique_ptr&lt;&gt ;.使用具有C ++值语义的对象来管理其他资源(如内存,打开文件,互斥锁上的锁等)的生命周期的模式称为RAII。

因此,简而言之,不同之处在于C ++有两种不同的方式来分配具有真正不同语义的对象(值/堆栈和引用/堆)。 Java(大多数)只有一个(引用/堆)。尽管它们都有.运算符,但Java .运算符的行为类似于C ++ ->运算符,因为它们通过指针(C ++)或引用访问其他位置的对象的字段(JAVA)。如果你尝试在空指针上使用->,或者在空Java引用上使用.,它们实际上可以以类似的方式爆炸。

C ++ .运算符访问值字段。它做了不同的事情,这就是它与众不同的原因。