
| 類 | Swfit | C++ |
|---|---|---|
| 關鍵字 | class | class / struct |
Swift 中使用class 后加類名來創(chuàng)建一個類. 類中的屬性聲明, 方法和函數(shù)聲明與普通的常量,變量,函數(shù)的聲明一樣:
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
練習: 使用
let添加一個常量屬性,再添加一個接收一個參數(shù)的方法。
而要創(chuàng)建一個類的實例, 在類名后面加上括號. 使用點語法來訪問實例的屬性和方法:
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
C++中使用 class或struct 后加類名來創(chuàng)建一個類, 它們的唯一區(qū)別就是默認訪問權限不太一樣. 類中的數(shù)據(jù)成員,成員函數(shù)與普通的變量,函數(shù)聲明一樣,只是不能使用 auto 來定義非常量非靜態(tài)的類成員; 而要創(chuàng)建一個類的實例, 在類名后面加上括號或者與普通聲明變量一樣, 使用類名. 使用點語法來訪問實例的屬性和方法:
#include <iostream>
#include <string>
using namespace std;
struct Shape {
static const auto constAutoNumber = 0;
int numberOfSides = 0;
auto simpleDescription() -> string {
return "A shape with " + to_string(numberOfSides) + "sides.";
}
};
int main() {
auto shape = Shape();
Shape shape2;
shape.numberOfSides = 7;
auto shapeDescription = shape.simpleDescription();
return 0;
}
練習: 在類中添加一個常量數(shù)據(jù)成員, 再添加一個成員函數(shù),它接受一個參數(shù).
Swift 中使用 init 來創(chuàng)建一個構造器來初始化實例, 注意self 被用來區(qū)別實例變量.當創(chuàng)建實例的時候, 先傳入函數(shù)參數(shù)一樣給類傳入構造器的參數(shù). 每個屬性都需要賦值—— 不管是通過聲明(就像numberOfSides)還是通過構造器(就像name); 而使用deinit 創(chuàng)建一個析構函數(shù)值刪除對象之前進行一些清理工作:
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
deinit {
// do some cleanup before the object is deallocated.
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
C++中如果沒有定義任何的構造函數(shù),編譯器會自動創(chuàng)建它; 對于大多數(shù)類來說, 對數(shù)據(jù)成員的初始化,如果存在類內(nèi)初始值,用它來初始化成員(就像 numberOfSides),否則,默認初始化該成員. 構造函數(shù)名和類名相同, 不寫返回類型, 默認構造函數(shù)沒有參數(shù),可以使用= default 來使用默認行為; 有參數(shù)的構造函數(shù)可以使用構造函數(shù)初始值列表; 構造函數(shù)初始值列表是在構造函數(shù)參數(shù)列表后面的初始化列表; 銷毀對象之前,可以在析構函數(shù)中進行一些必須的操作, 析構函數(shù)是一個波浪號接類名構成,它沒有返回值,也不接受參數(shù); 一個類只有唯一一個析構函數(shù):
#include <iostream>
#include <string>
using namespace std;
struct NamedShape {
NamedShape() = default;
NamedShape(const string name): name(name) { // 冒號后是構造函數(shù)初始值列表
}
~NamedShape() {
}
static const auto constAutoNumber = 0;
int numberOfSides = 0;
string name;
auto simpleDescription() -> string {
return "A shape with " + to_string(numberOfSides) + "sides.";
}
};
int main() {
auto shape = Shape();
Shape shape2("shape2");
auto shape3 = Shape("shape3");
shape.numberOfSides = 7;
auto shapeDescription = shape.simpleDescription();
return 0;
}
Swift 中子類定義的方法是在他們的類名后面加上父類的名字,用冒號分割.創(chuàng)建類的時候并不需要一個標準的根類; 而子類如果要重寫父類的方法的話, 需要用override 標記 —— 如果沒有添加override 就重寫父類方法的話, 編譯器會報錯, 同樣編譯器也能檢測到你用override 標記的方法是否確實在父類中:
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
練習: 創(chuàng)建
NamedShape的另一個子類Circle,構造器接收兩個參數(shù),一個是半徑一個是名稱,在子類Circle中實現(xiàn)area()和simpleDescription()方法。
C++中子類的定義方法和 Swift 中子類定義方法相同, 不過, 在初始化父類的數(shù)據(jù)成員時, 必須調(diào)用父類的構造函數(shù)來初始化.子類重寫父類的成員函數(shù)可以在成員函數(shù)后面顯式添加override 關鍵字, 不過, 子類中要重寫的成員函數(shù),父類中必須使用virtual標記; 注意this 的使用, 它是指向本對象自身的指針:
#include <iostream>
#include <string>
using namespace std;
struct NamedShape {
NamedShape() = default;
NamedShape(const string name): name(name) {
}
~NamedShape() {
}
static const auto constAutoNumber = 0;
int numberOfSides = 0;
string name;
virtual auto simpleDescription() -> string {
return "A shape with " + to_string(numberOfSides) + "sides.";
}
};
struct Square: NamedShape {
double sideLength;
Square(double sideLength, string name) : sideLength(sideLength), NamedShape(name) {
this->numberOfSides = 4;
}
auto area() -> double {
return sideLength * sideLength;
}
auto simpleDescription() -> string override {
return "A square with sides of length " + to_string(sideLength) + ".";
}
};
int main() {
auto test = Square(5.2, "my test square");
test.area();
test.simpleDescription();
return 0;
}
練習:創(chuàng)建
NamedShape的另一個子類Circle,構造器接收兩個參數(shù),一個是半徑一個是名稱,在子類Circle中實現(xiàn)area()和simpleDescription()方法。
Swift 中除了存儲屬性之外, 屬性還可以有 getter 和 setter:
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
在perimeter 的 setter 中,新值的名字是 newValue 。你可以在 set 之后顯式的設置一個名字。注意 EquilateralTriangle 類的構造器執(zhí)行了三步:
- 設置子類聲明的屬性值
- 調(diào)用父類的構造器
- 改變父類定義的屬性值。其他的工作比如調(diào)用方法、getters和setters也可以在這個階段完成。
如果你不需要計算屬性,但是仍然需要在設置一個新值之前或者之后運行代碼,使用willSet 和 didSet 。比如,下面的類確保三角形的邊長總是和正方形的邊長相同:
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
處理變量的可選值時,你可以在操作(比如方法、屬性和子腳本)之前加 ? 。如果 ? 之前的值是 nil, ? 后面的東西都會被忽略,并且整個表達式返回 nil 。否則, ? 之后的東西都會被運行。在這兩種情況下,整個表達式的值都是一個可選值:
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
Swift 的屬性有存儲屬性和計算屬性兩種, 存儲屬性相當于 C++中的數(shù)據(jù)成員, 而計算屬性相當于C++中寫的成員函數(shù). Swift 中的方法或函數(shù), 也相當于** C++**中的成員函數(shù).