GVKun编程网logo

swift (14) 构造函数(构造函数=default)

5

对于swift(14)构造函数感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解构造函数=default,并且为您提供关于c–使用SFINAE“重载”构造函数、c–错误:为“构造函数”属性指定

对于swift (14) 构造函数感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解构造函数=default,并且为您提供关于c – 使用SFINAE“重载”构造函数、c – 错误:为“构造函数”属性指定的参数数量错误、c# – 构造函数/析构函数链接错误、C#构造函数的宝贵知识。

本文目录一览:

swift (14) 构造函数(构造函数=default)

swift (14) 构造函数(构造函数=default)

类和结构体必须给他们的所有存储变量赋值,不允许类实例初始化完成后,还有变量没有被初始化,否则会不能通过编译。

你可以在构造函数中初始化一个存储型变量,也可以通过在定义时赋初始值的方法初始化变量。

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// prints "The default temperature is 32.0° Fahrenheit"

下面的效果跟上例类似,明显的,下面的方法简单明了,如果每个实例的初始化的值是相同的,则在定义时赋初始值是推荐的

struct Fahrenheit {
    var temperature = 32.0
}

初始化使用参数方式如下,可以有多个初始化,使用不同的参数名,下例是一个对温度不同的赋值方法:

struct Celsius {
    var temperatureInCelsius: Double = 0.0
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0

初始化函数的参数名在区分初始化方法的时候起到了重要作用,所以 swift 提供了自动扩展名的方法,如果你不提供扩展名,则扩展名跟本地参数名相同。如果不想提供扩展名,则参数名使用下划线 (_) 替代。

如果指定了扩展参数名,则必须使用,否则报编译错误

struct Color {
    let red = 0.0, green = 0.0, blue = 0.0
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
}

let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
//使用下面方法将会提示编译错误
let veryGreen = Color(0.01.00.0)

如果有可选变量,则可选变量时自动被初始化为 nil 的

即使是常量也可以在初始化时改变其初始值。常量属性也只能在本类的初始化函数中改变,不能在子类的初始化函数中改变

class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        println(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"

类有默认的构造函数,前提是所有的成员变量都有默认值,则类有一个没有参数,没有函数体的构造函数,类似 C 的默认构造函数,初始化其成员的值为默认值,并生成一个实例

结构体的成员构造函数,结构体的成员有初始值,并且没有定义构造函数时,结构体自动提供成员变量的构造函数:

struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

一个构造函数可以调用其他构造函数,这叫做构造代理,使用 self.init。

其中值类型的构造代理比较简单,因为它没有继承,所以只能调用自己的构造函数。在此需要注意,如果你定义了自己的构造函数,则类的默认构造函数和成员构造函数就不可再使用,如果你想既有自己的构造函数,又需要默认构造函数,则需要使用扩展方法。

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

let basicRect = Rect()
// basicRect''s origin is (0.0, 0.0) and its size is (0.0, 0.0)

let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))
// originRect''s origin is (2.0, 2.0) and its size is (5.0, 5.0)

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))
// centerRect''s origin is (2.5, 2.5) and its size is (3.0, 3.0)

Rect 有 3 个构造函数,第三个构造函数可以调用第二个构造函数。

指定构造函数就是我们一直使用的构造函数,没有修饰的, 所有类都必须至少有一个的构造函数

便利构造函数是为了方便用户初始化而添加的函数,使用 convenience 修饰的构造函数。

你可以使用任何一个构造函数定义实例。他们的格式为:

//指定构造方法
init(parameters) {
    statements
}
//便利构造方法
convenience init(parameters) {
    statements
}

指定构造函数和便利构造函数总是遵循下列规则:

1、指定构造函数肯定调用其直接父类的指定构造函数

2、便利构造函数肯定调用本类的其他任意构造函数

3、便利构造函数调用链最后必须以调用指定构造函数结束。

以代理的思想说就是:

指定构造函数总是向上代理,便利构造函数总是横向代理。

image: ../Art/initializerDelegation01_2x.png

编译器为了保证初始化正确进行,将初始化分为两个阶段,第一个阶段保证每个成员的都有其初始值,第二阶段提供给用户根据其需求修改成员的初始值。

编译器为了能正确进行初始化,进行如下 4 项安全检查:

1、指定构造方法在调用父类构造方法前,一定保证其成员都有初始值。

2、指定构造方法一定要在调用父类构造方法后,再给从父类继承的成员赋自定义值,如果不这样的话,你赋的值将被父类构造方法覆盖为父类的初始值。

3、便利构造方法先调用其他构造方法,再给成员变量赋值,否则你的赋值会被指定构造方法覆盖。

4、在第一阶段完成前,构造方法不能使用 self,不能使用实例方法,实例成员。

下面是根据上述 4 项检查,描述构造的两个阶段:

阶段一:

1、一个指定构造方法或者便利构造方法被调用

2、一个实例的内存被分配,但是内存的值还没有被初始化

3、总会有一个指定构造方法被调用,它保证所有存储变量有特定的值,内存的值现在被初始化了

4、这个指定构造方法开始调用父类构造方法,父类的构造方法保证父类本身的每个存储变量被初始化

5、重复步骤 3 和 4,直到最顶层的父类,最顶层的父类的存储变量被初始化后,这个实例的所有的变量也就被初始化完成,标志初始化的第一阶段完成。

image: ../Art/twoPhaseInitialization01_2x.png

阶段二:

6、从最顶层的父类开始,根据用户特殊需求,指定构造方法可以修改变量的值,调用实例方法,可以使用 self。

7、返回第二层父类,再返回第三层父类,最后到达本类的指定构造方法,便利构造方法,便利构造方法此时也可以使用 self,使用成员变量和方法。

image: ../Art/twoPhaseInitialization02_2x.png

构造方法的继承:

swift 不会默认继承父类的构造方法,构造方法的自动继承满足下面规则:

1、如果子类没有定义任何指定构造方法,则子类自动继承所有父类的指定构造方法

2、如果子类提供了所有父类指定构造方法的实现(无论是通过规则 1 实现,还是自定义实现,即使实现为便利构造方法),子类将自动继承父类所有便利构造方法

即使添加了更多便利构造方法,对上面的规则没有影响。

构造方法的重载:

你可以在子类中提供和重载父类的构造函数,如果重载指定构造函数,你可以重写实现和调用父类的这个指定构造函数。如果重载的是便利构造方法,则必须调用本类的一个另一个指定构造方法。

下面是一个示例,分析有助于理解:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

便利构造方法调用指定构造方法

image: ../Art/initializersExample01_2x.png

class RecipeIngredientFood {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

便利构造方法调用指定构造方法,子类的指定构造方法调用父类的指定构造方法

由于便利构造方法 init (name:String) 与父类指定构造方法使用了相同的参数名,所以认为是重写了父类的指定构造函数,所以父类的便利构造函数被自动继承

image: ../Art/initializersExample02_2x.png

class ShoppingListItemRecipeIngredient {
    var purchased = false
    var description: String {
    var output = "\(quantity) x \(name.lowercaseString)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}

购买清单这个类的成员都有初始值,并且没有自己的构造方法,则继承了父类的所有构造方法

image: ../Art/initializersExample03_2x.png

使用方法和闭包设置成员默认值

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
        }()
}

闭包最后的空括号告诉 swift 直接运行这个闭包,如果没有这个空括号,则表示将一个闭包赋值给 someProperty 了

注意:使用闭包初始化成员的值,不能使用 self 和其他成员,因为这时候实例还没有初始值

c – 使用SFINAE“重载”构造函数

c – 使用SFINAE“重载”构造函数

为什么以下 attempt重载构造函数Foo :: Foo失败?此外,我很欣赏替代方案/解决方法

#include <vector>
#include <type_traits>

namespace xyz
{   
    struct MemoryManager{};

    template<typename T,typename Alloc=MemoryManager>
    class vector
    {
    };
}


template<typename T,template<typename,typename> class V,typename A>
struct Foo
{
    template<typename U = T,typename Dummy = typename std::enable_if<
                 std::is_same<std::vector<U>,V<U,A> >::value >::type >
    Foo() // when instantiated with the std vector
    {
    }

    template<typename U = T,typename Dummy = typename std::enable_if<
                 std::is_same<xyz::vector<U>,A> >::value >::type >
    Foo() // when instantiated with my custom vector
    {
    }   
};

int main()
{
}

错误信息:

23:3: error: ‘template<class T,template<class,class> class V,class A> template<class U,class Dummy> Foo<T,V,A>::Foo()’ cannot be overloaded
   Foo() // when instantiated with my custom vector

18:3: error: with ‘template<class T,A>::Foo()’
   Foo() // when instantiated with the std vector

解决方法

您不能以这种方式重载构造函数,因为默认参数不是签名的一部分.实际上,你有:

template <typename,typename>
Foo();

template <typename,typename>
Foo();

这个规则可能在模板世界之外更清晰,这两个函数不能相互重载:

void foo(int x = 42);
void foo(int y = 17);

您可以改为将第二个模板参数设为默认的非类型参数,以便两个构造函数具有不同的签名:

template<typename U = T,std::enable_if_t<std::is_same<std::vector<U>,V<T,A> >::value,int> = 0>
Foo() // when instantiated with the std vector

或者您可以使用虚拟类型委托两个构造函数:

template <typename T> struct tag { };

Foo()
: Foo(tag<V<T,A>>{})
{ }

Foo(tag<std::vector<T>> )
{ /* std vector */ }

Foo(tag<xyz::vector<T>> )
{ /* your vector */ }

c – 错误:为“构造函数”属性指定的参数数量错误

c – 错误:为“构造函数”属性指定的参数数量错误

在实际实现之前,我编写了一个小的原型代码,并将类构造函数和ctor构造函数放在同一个文件中,以查看ctor是否先执行,这是我的实际实现.

但是,我正面临一个错误.这是代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <iostream>

using namespace std;

extern "C" void startMe(void) __attribute__ ((constructor(1)));
extern "C" void ending(void) __attribute__ ((destructor));

class Test {

 public:

    test()
    {
      cout << "This is test constructor" << endl;
    }
 };

 int main()
 {
  test();
  printf("Now main called\n");
 }

 void startMe(void)
 {
  printf("Start me called before main\n");
 }

 void ending(void)
 {
  printf("Destructor called\n");
 }

Output:
 $g++ constructor1.cc 
 constructor1.cc:10: error: wrong number of arguments specified for ‘constructor’ attribute

但是,当我删除构造函数优先级时,它编译并运行正常.也就是说,我这样做:

extern "C" void startMe(void) __attribute__ ((constructor));

为什么会这样?如何优先考虑?

请帮我.我的想法是首先执行“ctor”,然后执行另一个(Test)构造函数.同样的原因,我把ctor作为优先事项1.

解决方法

按原样编译程序会产生:

warning: constructor priorities from 0 to 100 are reserved for the implementation

将优先级从1更改为101可以消除警告并且可执行文件生成:

Start me called before main
 This is test constructor
 Now main called
 Destructor called

这是使用GCC 4.5

c# – 构造函数/析构函数链接错误

c# – 构造函数/析构函数链接错误

我有一个项目(创建一个DLL),比如说test.dll,我没有在dll中输出类的构造函数和析构函数,比如TestClass.这是因为我有一些工厂函数应该被调用来创建和销毁TestClass的对象.这个设计在我创建的独立示例中完美地运行,我尝试使用test.dll中的TestClass对象.

但是,当我将这个test.dll(或者我的情况下的.lib,因为我使用Visual Studio)链接到我们的生产模块中的项目时,我得到奇怪的链接错误指向它无法为TestClass找到的构造函数和析构函数.我知道我不会调用new / delete,或者在项目的任何地方创建任何TestClass的堆栈实例.生产模块使用C#/ CLR / CLI以及C.解决方法的唯一方法是为我导出TestClass的构造函数和析构函数.这在设计上是不合需要的.

这种情况对任何人都很熟悉吗?有人可以指出可能存在的问题吗?

这是我得到的错误:

Error   5264    error LNK2028: unresolved token (0A000BA3) "public: virtual __thiscall BE::TestClass::~TestClass(void)" (??1TestClass@BE@@$$FUAE@XZ) referenced in function "public: virtual void * __thiscall BE::TestClass::`vector deleting destructor'(unsigned int)" (??_ETestClass@BE@@$$FUAEpaxI@Z)  AMBestDetailBridge.obj  BEBase

Error   5373    error LNK2001: unresolved external symbol "public: virtual __thiscall BE::TestClass::~TestClass(void)" (??1TestClass@BE@@$$FUAE@XZ) AMBestDetailBridge.obj  BEBase

谢谢!

解决方法

链接错误说你通过delete []调用析构函数,所以很可能在你的生产模块中,你有一些代码:

TestClass* pTest = ...
delete pTest;
TestClass* pTestArray = ...
delete[] pTest;

当然,在智能指针的情况下,它可能不是那么明显:

SmartPtr<TestClass> spTest = ...
// delete called automatically when out of scope.

在一个模块中分配并在另一个模块中删除会使自己面临潜在的风险 – 因为2个模块可能使用不同的堆进行内存分配(比如重新定义新的运算符),幸运的是这个问题在链接时暴露,而不是在运行时暴露.

C#构造函数

C#构造函数

using System;

namespace LineApplication {

   class Line {
      private double length;   // Length of a line

      public Line() {
         Console.WriteLine(Object is being created);
      }

      public void setLength( double len ) {
         length = len;
      }

      public double getLength() {
         return length;
      }

      static void Main(string[] args) {
         Line line = new Line();    

         // set line length
         line.setLength(6.0);
         Console.WriteLine(Length of line : {0}, line.getLength());
         Console.ReadKey();
      }
   }
}

今天关于swift (14) 构造函数构造函数=default的分享就到这里,希望大家有所收获,若想了解更多关于c – 使用SFINAE“重载”构造函数、c – 错误:为“构造函数”属性指定的参数数量错误、c# – 构造函数/析构函数链接错误、C#构造函数等相关知识,可以在本站进行查询。

本文标签: