12 Jun 2020
|
Flutter
group.dart
import 'package:flutter/material.dart';
class Group {
final String name;
final IconData icon;
final peopleList = List<String>();
void addPerson(String name) {
peopleList.add(name);
}
Group({this.name, this.icon});
}
main.dart
import 'package:flutter/material.dart';
import 'group.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expandable ListView',
theme: ThemeData(
primarySwatch: Colors.pink,
),
home: Scaffold(
appBar: AppBar(
title: Text("Expandable ListView Example"),
),
body: MyExample(),
),
);
}
}
class MyExample extends StatelessWidget {
final Group group1 = Group(name: 'Teacher', icon: Icons.people);
final Group group2 = Group(name: 'Student', icon: Icons.people_outline);
final groupList = List<Group>();
MyExample() {
group1.addPerson('snowdeer');
group1.addPerson('snowcat');
group1.addPerson('snowlion');
group2.addPerson('ran');
group2.addPerson('song');
group2.addPerson('down');
group2.addPerson('john');
group2.addPerson('yang');
groupList.add(group1);
groupList.add(group2);
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: groupList.length,
itemBuilder: (context, i) {
final group = groupList[i];
return ExpansionTile(
title: Text(group.name),
children: [
Column(
children: _buildExpandableContent(group),
)
],
);
},
),
),
);
}
_buildExpandableContent(Group group) {
final columnContent = List<Widget>();
for (String name in group.peopleList)
columnContent.add(
ListTile(
title: Text(
name,
style: TextStyle(fontSize: 18.0),
),
leading: Icon(group.icon),
),
);
return columnContent;
}
}
11 Jun 2020
|
C++
모던 C++ 입문 책을 읽고 실수하기 쉽거나 유용한 팁, 더 나은 방법 등을 정리해보았습니다.
클래스(class)와 구조체(struct)
C++에서 클래스(class)와 구조체(struct)의 유일한 차이는 구조체의 모든 멤버들의 접근 지정자가 public
라는 점 뿐입니다.
가능하면 클래스를 사용하는 것이 좋으며 Getter와 Setter 함수로 데이터에 접근하는 것이 불편한 헬퍼 타입에 대해서만 구조체를 쓰는 것이 좋습니다.
friend
는 클래스의 private
, protected
, public
데이터에 접근이 가능하기 때문에 가급적 사용하지 않는 것이 좋습니다.
Default Construct
디폴트 생성자는 인수가 없는 생성자 또는 모든 인수가 기본 값을 가지는 생성자입니다.
디폴트 생성자가 없어도 클래스를 사용할 수는 있지만 가급적 디폴트 생성자를 정의하는 편이 좋습니다.
특히 리스트, 트리, 벡터, 행렬 등 디폴트 생성자가 없는 타입의 컨테이너를 구현하는 것은 아주 번거롭기 때문에
디폴트 생성자는 가급적 정의하는 것이 좋습니다.
복사 생성자
객체를 복사하는 것보다 복사 생성자를 사용하는 방법이 더 좋습니다.
class Complex {
public:
Complex(const Complex & c) : i(c.i), r(c.r) {}
private:
// ...
}
클래스 내부의 변수는 포인터보다는 unique_ptr
이 메모리 누수 예방이나 실수 방지 면에서 더 낫습니다.
생성자 위임(Delegating Constructor)
class Complex {
public:
Complex(double r, double i) : r{ r }, i{ i } {}
Complex(double r) : Complex{ r, 0.0 } {}
Complex() : Complex{ 0.0 } {}
}
소멸자
소멸자는 다음 두 가지를 유의해야 합니다.
- 절대 Exception을 발생시키지 말아라.
- 클래스에
virtual
함수가 포함되어 있으면 소멸자도 virtual
이어야 한다.
첨자 연산자
class vector {
public:
double at(int i) {
assert(i >= 0 && i < my_size);
return data[i];
}
};
와 같은 함수를 첨자 연산자를 이용해서 다시 표현할 수 있습니다.
class vector {
public:
double operator[](int i) {
assert(i >= 0 && i < my_size);
return data[i];
}
};
보다 코드가 더 간결해지며 코드의 의미를 더 명확하게 표현할 수 있습니다.
10 Jun 2020
|
C++
모던 C++ 입문 책을 읽고 실수하기 쉽거나 유용한 팁, 더 나은 방법 등을 정리해보았습니다.
포인터
포인터는 메모리 주소 값을 가지는 변수입니다. 포인터를 사용하면 강력한 일들을 수행할 수 있지만 메모리 누수(Memory Leak)와 같은
위험이 자주 발생할 수 있어서 점점 사용을 제한하는 추세입니다.
포인터 관련 오류를 최소화하기 위해서 다음과 같은 방법들이 있습니다.
std::vector
와 같은 표준 컨테이너를 사용해라.
- 클래스에서 동적 메모리는 개체 생성시 할당하고 파괴할 때 해제해야 한다. 이러한 원칙을 RAII(Resource Acquisition Is Initialization)이라고 합니다.
- 스마트 포인터를 사용해라.
- 레퍼런스(Reference)를 사용해라.
NULL
매크로 대신 nullptr
을 사용해라.
스마트 포인터
개인적으로 C++11로 넘어오면서 가장 큰 변화가 스마트 포인터와 Thread 표준화가 아닌가 생각이 듭니다.
unique_ptr
, shared_ptr
, weak_ptr
이 있습니다.
unique_ptr
데이터의 고유 소유권(Unique Ownership)을 나타내며, 포인터 만료시 메모리가 자동 해제됩니다.
int main() {
unique_ptr<double> dp{ new double };
*dp = 7;
// ...
}
다른 포인터 타입에 할당하거나 암시적 변환은 불가능하며, 원시 포인터 데이터를 얻고 싶을 경우 `get()` 함수를 이용하면 됩니다.
double * raw_dp = dp.get();
다른 `unique_ptr`에 할당할 수도 없으며, 오직 이동(`move`)만 가능합니다.
unique_ptr<double> dp2{ move(dp) }, dp3;
dp3 = move(dp2);
위에서 참조한 메모리의 소유권을 `dp`에서 `dp2`로 전달한 다음 `dp3`에 전달합니다.
그 이후 `dp`와 `dp2`는 `nullptr`이 됩니다.
### shared_ptr
`shared_ptr`은 일반적으로 가장 많이 사용하게 될 스마트 포인터입니다.
### weak_ptr
`shared_ptr`에서 발생할 수 있는 문제 중 하나는 순환 참조(Cycle Reference)입니다. 순환 참조가 발생하면
메모리 해제가 되지 않아 메모리 누수(Memory Leak)이 발생할 수 있습니다. `weak_ptr`은 공유를 하더라도
소유권을 주장하지 않기 때문에 순환 참조를 막을 수 있습니다.
## 레퍼런스
레퍼런스가 포인터에 비해 갖는 주요 이점 중 하나는 동적 메모리 관리 및 주소 계산 기능입니다.
포인터에 비해 메모리 누수 가능성이 거의 없고, 포인터에 비해 표기법이 깔끔한 장점이 있습니다.
특징 | 포인터 | 레퍼런스
---|---|---
정의된 위치 참조 | | O
초기화 필수 | | O
메모리 누수 예방 | | O
개체와 같은 표기법 | | O
메모리 관리 | O |
주소 계산 | O |
컨테이너 만들기 | O |
### Stale Reference 및 Dangline Pointer
함수 내의 지역 변수는 함수 범위(Scope) 내에서만 유효합니다.
아래와 같은 코드는 절대 사용하지 않도록 합시다.
double & square_ref(double d) {
double s = d * d;
return s;
}
또는
double * square_ref(double d) {
double s = d * d;
return &s;
}
## 벡터 초기화
벡터 초기화는 요소별로 값을 설정하는 것보다 C++11부터 지원하는 Initializer List를 이용해서 초기화하는 것이 더 좋습니다.
vector<float> v = {1, 2, 3};
## 매크로
매크로는 대부분의 언어에서 최소한으로 제한하는 것이 좋습니다. 매크로는 이름을 인수와 함께 텍스트 정의 확장해서 코드를 재사용하는
고전 기법 중 하나일 뿐입니다.
대부분의 매크로는 `const`, `inline`, `constexpr` 등으로 대체해서 사용할 수 있습니다.
### include
`include` 전처리기는 `/usr/include`, `/usr/local/include` 등과 같은 표준 디렉토리에서 파일을 검색합니다.
컴파일러 옵션을 이용해서 디렉토리를 추가할 수도 있습니다.
만약 `include` 구문에 큰 따옴표를 쓰게 되면, 일반적으로 컴파일러는 현재 디렉토리에서 먼저 검색한 다음 표준 경로에서 검색합니다.
부등호는 시스템 헤더, 큰 따옴표는 사용자 헤더에 사용해야 한다고 주장하는 사람들도 있습니다.
자주 사용되는 헤더 파일이 프로젝트 내에서 여러 번 호출되는 것을 방지하기 위해서 `#ifndef` 등의 포함 방지(Include Guard) 매크로를
사용할 수 있습니다. 좀 더 편리하게 사용하는 방법으로 `#progma once`가 있으며, `progma`는 표준이 아니지만
대부분의 컴파일러가 지원하고 있습니다.
조건부 컴파일인 `#ifdef`, `#else` 등은 소스 코드 관리 및 테스트가 어려워지기 때문에 가급적 사용하지 않는 것이 좋습니다.
08 Jun 2020
|
Flutter
todo_item.dart
class TodoItem {
String name = '';
bool isChecked = false;
TodoItem({this.name});
}
main.dart
import 'package:fileio/todo_item.dart';
import 'package:flutter/material.dart';
void main() => runApp(SnowApp());
class SnowApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo list',
theme: ThemeData(
primaryColor: Colors.deepPurple,
),
home: Scaffold(
appBar: AppBar(
title: Text('Todo list'),
),
body: TodoListWidget(),
));
}
}
class TodoListWidget extends StatefulWidget {
@override
State createState() => TodoListWidgetState();
}
class TodoListWidgetState extends State<TodoListWidget> {
final controller = TextEditingController();
final list = List();
@override
void dispose() {
super.dispose();
controller.dispose();
}
void addTodo(TodoItem item) {
setState(() {
list.add(item);
});
}
void removeTodo(TodoItem item) {
setState(() {
list.remove(item);
});
}
void setChecked(TodoItem item, bool isChecked) {
setState(() {
item.isChecked = isChecked;
});
}
Widget buildListTime(BuildContext context, TodoItem item) {
return ListTile(
onTap: () {
setChecked(item, !item.isChecked);
},
leading: item.isChecked == true
? Icon(Icons.check_box)
: Icon(Icons.check_box_outline_blank),
title: Text(
item.name,
style: item.isChecked
? TextStyle(
decoration: TextDecoration.lineThrough,
fontStyle: FontStyle.italic,
)
: null,
),
trailing: IconButton(
icon: Icon(Icons.delete_forever),
onPressed: () {
removeTodo(item);
},
),
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: controller,
),
),
RaisedButton(
child: Text(
'Add',
),
onPressed: () {
addTodo(TodoItem(name: controller.text));
controller.text = '';
},
),
],
),
Expanded(
child: ListView(
children: list.map((item) => buildListTime(context, item)).toList(),
),
)
],
);
}
}
07 Jun 2020
|
Flutter
TextFile 쓰기/읽기
import 'dart:io';
import 'package:flutter/material.dart';
void main() => runApp(SnowApp());
class SnowApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'File IO Example',
theme: ThemeData(
primaryColor: Colors.indigoAccent,
),
home: Scaffold(
appBar: AppBar(
title: Text('File IO Example'),
),
body: FileIoTest(),
),
);
}
}
class FileIoTest extends StatelessWidget {
void saveToFile(String filepath, String text) {
final file = File(filepath);
file.createSync();
file.writeAsStringSync(text, mode: FileMode.append);
}
void loadFromFile(String filepath) {
final file = File(filepath);
print('Filepath: ${file.absolute.path}');
final lines = file.readAsLinesSync();
for (String line in lines) {
print(line);
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
RaisedButton(
child: Text('Save to file'),
onPressed: () {
final filepath = 'snowdeer.txt';
final text = "Hello, SnowDeer\n";
saveToFile(filepath, text);
},
),
RaisedButton(
child: Text('Load from file'),
onPressed: () {
final filepath = 'snowdeer.txt';
loadFromFile(filepath);
},
),
],
),
);
}
}