类 | 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++中使用 class
或struct
后加类名来创建一个类, 它们的唯一区别就是默认访问权限不太一样. 类中的数据成员,成员函数与普通的变量,函数声明一样,只是不能使用 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
类的构造器执行了三步:
- 设置子类声明的属性值
- 调用父类的构造器
- 改变父类定义的属性值。其他的工作比如调用方法、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++中的数据成员, 而计算属性相当于C++中写的成员函数. Swift 中的方法或函数, 也相当于** C++**中的成员函数.
网友评论