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


112

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

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

답변:


78

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에서 실제 형식의 표준화 (표현)가 아닙니다.

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


80

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 변수를 만들 수 있기 때문입니다.

그렇다면 컴파일 타임 상수는 무엇에 좋을까요?

  • 열거 형에 유용합니다.
  • 스위치 케이스에서 컴파일 타임 상수 값을 사용할 수 있습니다.
  • 주석으로 사용됩니다.

Dart가 변수 초기화를 지연시키기 전에 컴파일 시간 상수가 더 중요했습니다. 그 전에는 "var x = foo;"와 같이 초기화 된 전역 변수 만 선언 할 수있었습니다. "foo"가 컴파일 타임 상수 인 경우. 이러한 요구 사항이 없으면 대부분의 프로그램은 const 객체를 사용하지 않고도 작성할 수 있습니다.

요약하자면 Const 생성자는 컴파일 시간 상수 값을 만들기위한 것입니다.

/엘

[1] 또는 실제로 : "잠재적으로 컴파일 시간 상수 표현식"은 생성자 매개 변수를 참조 할 수도 있기 때문입니다. [2] 그렇습니다. 클래스는 동시에 const와 non-const 생성자를 모두 가질 수 있습니다.

이 주제는 https://github.com/dart-lang/sdk/issues/36079 에서도 몇 가지 흥미로운 의견과 함께 논의되었습니다 .


AFAIK const 및 final을 사용하면보다 최적화 된 JS를 생성 할 수 있습니다.
Günter Zöchbauer 2014

2
메서드 서명의 기본값에도 유용합니다.
Florian Loitsch 2014

1
누구든지이 라인이 어떻게 작동하는지 설명 할 수 있습니까? Point.clone(Point other): x = other.x, y = other.y;
Daksh Gargas

어떤 부분이 명확하지 않습니까? 그것은 관련이 보이지 않는다const
귄터 Zöchbauer

3
const플러터을위한 좋은 성능의 승리에 따라 위젯입니다 medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1 const를하지 않고 위젯, 발생하지 않습니다 하위 트리의 선택적 재건을 구축하기 "를 사용 CONST. 떨림은 각각의 새로운 인스턴스를 생성 특히 빌드 메소드가 무거운 경우 귀중한 사이클을 낭비하는 build ()를 호출합니다. "
David Chandler

8

자세히 설명되어 있지만 실제로 const 생성자의 사용법을 찾고있는 사용자를위한 것입니다.

Flutter가 업데이트해야하는 위젯 만 다시 빌드하는 데 도움이되므로 Flutter 성능을 높이는 데 사용됩니다. StateFulWidgets에서 setState ()를 사용하는 동안 const 생성자가 아닌 구성 요소 만 다시 빌드됩니다.

예를 들어 설명 할 수 있습니다->

    class _MyWidgetState extends State<MyWidget> {

  String title = "Title";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: <Widget>[
          const Text("Text 1"),
          const Padding(
            padding: const EdgeInsets.all(8.0),
            child: const Text("Another Text widget"),
          ),
          const Text("Text 3"),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          setState(() => title = 'New Title');
        },
      ),
    );
  }
}

이 예제에서와 같이 텍스트 제목 만 변경되어야하므로이 위젯 만 다시 빌드해야합니다. 따라서 다른 모든 위젯을 const 생성자로 만들면 성능 향상을 위해 동일한 작업을 수행하는 데 도움이됩니다.


0

const 인스턴스가 실제로 최종 필드로 결정하는 예제 데모입니다.
이 경우 컴파일 타임에 예측할 수 없습니다.

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)
  });
}

이제 다트가 확인합니다.

다트 분석 :

[dart] 'j'필드가 상수가 아닌 값으로 초기화되어 'const'생성자를 정의 할 수 없습니다.

런타임 오류:

/main.dart ': 오류 : 5 행 위치 17 : 표현식이 유효한 컴파일 시간 상수가 아닙니다. final int j = new DateTime.now (). millisecond;

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.