Development Tip

const 생성자는 실제로 어떻게 작동합니까?

yourdevel 2020. 10. 19. 12:51
반응형

const 생성자는 실제로 어떻게 작동합니까?


나는 Dart에서 const 생성자를 만드는 것이 가능하다는 것을 알아 챘다. 문서에서는 const컴파일 시간 상수를 나타내는 데 단어가 사용 되었다고 말합니다 .

const생성자를 사용하여 객체를 생성하면 어떻게되는지 궁금 합니다. 이것이 항상 동일하고 컴파일 타임에 사용할 수있는 불변 객체와 같은가요? const생성자 의 개념은 실제로 어떻게 작동합니까? 방법이다 CONST의 생성자는 다른 일반 생성자?


Const 생성자는 "표준화 된"인스턴스를 만듭니다.

즉, 모든 상수 표현식은 정규화를 시작하고 나중에 이러한 "정규화 된"기호를 사용하여 이러한 상수의 동등성을 인식합니다.

정규화 :

둘 이상의 가능한 표현이있는 데이터를 "표준"표준 표현으로 변환하는 프로세스입니다. 이는 등가성에 대해 서로 다른 표현을 비교하고, 고유 한 데이터 구조의 수를 계산하고, 반복되는 계산을 제거하여 다양한 알고리즘의 효율성을 개선하거나, 의미있는 정렬 순서를 부과 할 수 있도록하기 위해 수행 될 수 있습니다.


이는 다음과 같은 const 표현식이 const Foo(1, 1)가상 머신에서 비교하는 데 유용한 사용 가능한 형식을 나타낼 수 있음을 의미합니다 .

VM은이 const 표현식에서 발생하는 순서대로 값 유형과 인수 만 고려하면됩니다. 그리고 물론 최적화를 위해 축소됩니다.

정규화 된 값이 동일한 상수 :

var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1

표준화 된 값이 다른 상수 (서명이 다르기 때문에) :

var foo3 = const Foo(1, 2); // $Foo$int$1$int$2
var foo4 = const Foo(1, 3); // $Foo$int$1$int$3

var baz1 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
var baz2 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello

상수는 매번 다시 생성되지 않습니다. 이들은 컴파일 타임에 표준화되고 나중에 재사용되는 특수 조회 테이블 (표준 서명에 의해 해시되는 위치)에 저장됩니다.

추신

#Foo#int#1#int#1이 샘플에 사용 된 형식 은 비교 목적으로 만 사용되며 Dart VM에서 실제 형식의 표준화 (표현)가 아닙니다.

그러나 실제 표준화 형식은 "표준"표준 표현이어야합니다.


Chris Storms 블로그에서 Lasse의 답변이 훌륭한 설명이라고 생각합니다.

Dart 상수 생성자

나는 그들이 내가 내용을 복사하는 것을 신경 쓰지 않기를 바랍니다.

이것은 최종 필드에 대한 훌륭한 설명이지만 실제로 const 생성자를 설명하지는 않습니다. 이 예제의 어떤 것도 실제로 생성자가 const 생성자라는 것을 사용하지 않습니다. 모든 클래스는 최종 필드, const 생성자를 가질 수 있습니다.

Dart의 필드는 저장소를 읽고 업데이트하는 자동으로 생성 된 getter 및 setter와 결합 된 실제로 익명의 저장소 위치이며 생성자의 이니셜 라이저 목록에서 초기화 할 수도 있습니다.

최종 필드는 setter없이 동일하므로 값을 설정하는 유일한 방법은 생성자 이니셜 라이저 목록에 있으며 그 이후에는 값을 변경할 방법이 없습니다. 따라서 "최종"이됩니다.

const 생성자의 요점은 최종 필드를 초기화하는 것이 아니라 모든 생성 생성자가이를 수행 할 수 있다는 것입니다. 요점은 컴파일 타임 상수 값을 생성하는 것입니다. 모든 필드 값이 명령문을 실행하지 않고 컴파일 타임에 이미 알려진 객체입니다.

이는 클래스와 생성자에 몇 가지 제한을 둡니다. const 생성자는 본문 (명령문이 실행되지 않음)을 가질 수 없으며 해당 클래스에는 최종 필드가 없어야합니다 (컴파일시 "알고있는"값은 나중에 변경할 수 없어야 함). 이니셜 라이저 목록은 필드를 다른 컴파일 타임 상수로만 초기화해야하므로 오른쪽은 "컴파일 타임 상수 식"[1]으로 제한됩니다. 그리고 "const"접두사가 있어야합니다. 그렇지 않으면 이러한 요구 사항을 충족하는 정상적인 생성자를 얻을 수 있습니다. 그것은 완벽하게 괜찮습니다. 그것은 단지 const 생성자가 아닙니다.

const 생성자를 사용하여 실제로 컴파일 타임 상수 객체를 생성하려면 "new"표현식에서 "new"를 "const"로 바꿉니다. const 생성자와 함께 "new"를 계속 사용할 수 있으며 여전히 객체를 생성하지만 컴파일 타임 상수 값이 아닌 일반적인 새 객체 일뿐입니다. 즉, const 생성자는 런타임에 객체를 생성하고 컴파일 타임에 컴파일 타임 상수 객체를 생성하기위한 일반 생성자로도 사용할 수 있습니다.

예를 들어 :

class Point { 
  static final Point ORIGIN = const Point(0, 0); 
  final int x; 
  final int y; 
  const Point(this.x, this.y);
  Point.clone(Point other): x = other.x, y = other.y; //[2] 
}

main() { 
  // Assign compile-time constant to p0. 
  Point p0 = Point.ORIGIN; 
  // Create new point using const constructor. 
  Point p1 = new Point(0, 0); 
  // Create new point using non-const constructor.
  Point p2 = new Point.clone(p0); 
  // Assign (the same) compile-time constant to p3. 
  Point p3 = const Point(0, 0); 
  print(identical(p0, p1)); // false 
  print(identical(p0, p2)); // false 
  print(identical(p0, p3)); // true! 
}

컴파일 시간 상수는 정규화됩니다. 즉, "const Point (0,0)"을 몇 번 써도 하나의 객체 만 생성됩니다. 그것은 유용 할 수 있습니다. 그러나 그것이 보이는 것만 큼은 아닙니다. 왜냐하면 여러분은 값을 보유하고 대신 변수를 사용하기 위해 const 변수를 만들 수 있기 때문입니다.

So, what are compile-time constants good for anyway?

  • They are useful for enums.
  • You can use compile-time constant values in switch cases.
  • They are used as annotations.

Compile-time constants used to be more important before Dart switched to lazily initializing variables. Before that, you could only declare an initialized global variable like "var x = foo;" if "foo" was a compile-time constant. Without that requirement, most programs can be written without using any const objects

So, short summary: Const constructors are just for creating compile-time constant values.

/L

[1] Or really: "Potentially compile-time constant expressions" because it may also refer to the constructor parameters. [2] So yes, a class can have both const and non-const constructors at the same time.

This topic was also discussed in https://github.com/dart-lang/sdk/issues/36079 with some interesting comments.


An example demo that const instance really decide by final field.
And in this case, it cannot be predict in compile-time.

import 'dart:async';

class Foo {
  final int i;
  final int j = new DateTime.now().millisecond;
  const Foo(i) : this.i = i ~/ 10;

  toString() => "Foo($i, $j)";
}



void main() {
  var f2 = const Foo(2);
  var f3 = const Foo(3);

  print("f2 == f3 : ${f2 == f3}"); // true
  print("f2 : $f2"); // f2 : Foo(0, 598)
  print("f3 : $f3"); // f3 : Foo(0, 598)

  new Future.value().then((_) {
    var f2i = const Foo(2);
    print("f2 == f2i : ${f2 == f2i}"); // false
    print("f2i : $f2i"); // f2i : Foo(0, 608)
  });
}

Now dart will check it.

Dart Analysis:

[dart] Can't define the 'const' constructor because the field 'j' is initialized with a non-constant value

Runtime Error:

/main.dart': error: line 5 pos 17: expression is not a valid compile-time constant final int j = new DateTime.now().millisecond;

참고URL : https://stackoverflow.com/questions/21744677/how-does-the-const-constructor-actually-work

반응형