iOS Developer程序员

【Swift 3 && C++11】<第一

2016-10-24  本文已影响39人  大刀和长剑
对象和类
Swfit C++
关键字 class class / struct

Swift 中使用class 后加类名来创建一个类. 类中的属性声明, 方法和函数声明与普通的常量,变量,函数的声明一样:

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

练习: 使用 let 添加一个常量属性,再添加一个接收一个参数的方法。

而要创建一个类的实例, 在类名后面加上括号. 使用点语法来访问实例的属性和方法:

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

C++中使用 classstruct 后加类名来创建一个类, 它们的唯一区别就是默认访问权限不太一样. 类中的数据成员,成员函数与普通的变量,函数声明一样,只是不能使用 auto 来定义非常量非静态的类成员; 而要创建一个类的实例, 在类名后面加上括号或者与普通声明变量一样, 使用类名. 使用点语法来访问实例的属性和方法:

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

练习: 在类中添加一个常量数据成员, 再添加一个成员函数,它接受一个参数.

Swift 中使用 init 来创建一个构造器来初始化实例, 注意self 被用来区别实例变量.当创建实例的时候, 先传入函数参数一样给类传入构造器的参数. 每个属性都需要赋值—— 不管是通过声明(就像numberOfSides)还是通过构造器(就像name); 而使用deinit 创建一个析构函数值删除对象之前进行一些清理工作:

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++中如果没有定义任何的构造函数,编译器会自动创建它; 对于大多数类来说, 对数据成员的初始化,如果存在类内初始值,用它来初始化成员(就像 numberOfSides),否则,默认初始化该成员. 构造函数名和类名相同, 不写返回类型, 默认构造函数没有参数,可以使用= default 来使用默认行为; 有参数的构造函数可以使用构造函数初始值列表; 构造函数初始值列表是在构造函数参数列表后面的初始化列表; 销毁对象之前,可以在析构函数中进行一些必须的操作, 析构函数是一个波浪号接类名构成,它没有返回值,也不接受参数; 一个类只有唯一一个析构函数:

#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;
    
    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 中子类定义的方法是在他们的类名后面加上父类的名字,用冒号分割.创建类的时候并不需要一个标准的根类; 而子类如果要重写父类的方法的话, 需要用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()

练习: 创建 NamedShape 的另一个子类 Circle ,构造器接收两个参数,一个是半径一个是名称,在子类 Circle 中实现 area()simpleDescription() 方法。

C++中子类的定义方法和 Swift 中子类定义方法相同, 不过, 在初始化父类的数据成员时, 必须调用父类的构造函数来初始化.子类重写父类的成员函数可以在成员函数后面显式添加override 关键字, 不过, 子类中要重写的成员函数,父类中必须使用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;
}

练习:创建 NamedShape 的另一个子类 Circle ,构造器接收两个参数,一个是半径一个是名称,在子类 Circle 中实现 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 类的构造器执行了三步:

  1. 设置子类声明的属性值
  2. 调用父类的构造器
  3. 改变父类定义的属性值。其他的工作比如调用方法、getters和setters也可以在这个阶段完成。

如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSetdidSet 。比如,下面的类确保三角形的边长总是和正方形的边长相同:

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++中的数据成员, 而计算属性相当于C++中写的成员函数. Swift 中的方法或函数, 也相当于** C++**中的成员函数.

上一篇下一篇

猜你喜欢

热点阅读