导航:首页 > 源码编译 > Vc编译器weak属性

Vc编译器weak属性

发布时间:2022-12-14 23:13:59

A. 什么时候在block中不需要使用weakSelf

Objective C 的 Block 是一个很实用的语法,特别是与GCD结合使用,可以很方便地实现并发、异步任务。但是,如果使用不当,Block 也会引起一些循环引用问题(retain cycle)——
Block 会 retain ‘self’,而 ‘self‘ 又 retain 了 Block。因为在 ObjC
中,直接调用一个实例变量,会被编译器处理成 ‘self->theVar’,’self’ 是一个 strong 类型的变量,引用计数会加
1,于是,self
retains queue, queue retains block,block retains self。

解决 retain circle

Apple 官方的建议是,传进 Block 之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 Block
中就不会出现对 self 的强引用。如果在 Block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。

示例代码:

1 2 3 4

__weak
__typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{ [weakSelf doSomething]; });

clang 的文档表示,在 doSomething 内,weakSelf 不会被释放。但,下面的情况除外:

1 2 3 4 5

__weak
__typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{ [weakSelf doSomething]; [weakSelf doOtherThing]; });

在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:

1 2 3 4 5 6

__weak
__typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{ __strong __typeof(self) strongSelf = weakSelf; [strongSelf
doSomething]; [strongSelf doOtherThing]; });

__strong 确保在 Block 内,strongSelf 不会被释放。

总结

在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

B. STM32的hal库中__weak函数前缀的作用

在使用STM32的hal库的时候,我们常常可以看到很多库自带的函数有很多是使用__weak修饰的,比如:

weak 顾名思义是“弱”的意思,所以如果函数名称前面加上__weak 修饰符,我们一般称这个函数为 “弱函数” 。

加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。

__weak是一个宏,和__packed是同一种东西都是gcc的扩展属性:

#define __packed __attribute__((packed))

#define __weak __attribute__((weak))

如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__weak那个), 如果有两个一样的函数都用了__weak,那么真正调用那个,就要看连接器了。

例子:

我们打开一个工程

可以看出,HAL_CAN_TxCpltCallback 函数前面有加修饰符__weak。同时,并且 CAN_Transmit_IT函数中调用了函数 HAL_CAN_TxCpltCallback。

如果我们没有在工程中其他地方重新定义 HAL_CAN_TxCpltCallback ()函数,那么 CAN_Transmit_IT初始化函数执行的时候,会默认执行 stm32f0xx_hal_can.c 文件中定义的 HAL_CAN_TxCpltCallback 函数,而这个函数没有任何控制逻辑。

如果用户在工程中重新定义函数 HAL_CAN_TxCpltCallback ,那么调用 CAN_Transmit_IT之后,会执行用户自己定义的HAL_CAN_TxCpltCallback 函数而不会执行 stm32f0xx_hal_can.c 默认定义的函数。也就是说,表面上我们看到函数 HAL_CAN_TxCpltCallback 被定义了两次,但是因为有一次定义是弱函数,使用了__weak修饰符,所以编译器不会报错。

愿你出走半生,归来仍是少年…

C. 编写一个程序,读取5个整数并确定和打印其中的最大值,以下是我写的代码,错了,谁能帮我改改

系统看成是各种对象的集合,这更接近人的思维。
2)软件需求的变动往往是功能的变动,而功能的执行者--对象一般不会有太大的变化。这使得按照对象设计出来的系统结构比较稳定。
3)对象包括属性和方法,对象把属性和方法的具体实现方式一起封装起来,这使得方法与之相关的属性不再分离,提高每个子系统的相对独立性,从而提高了软件的可维护性。
4)支持封装、继承、多态和抽象,提高了软件的可重用性、可维护性和可扩展性。

2.把一个类放在包里有什么作用?(包的作用)
1)能够区分名字相同的类。
2)有助于实施访问权限控制。
3)有助于划分和组织java应用中的各个类。

3.说出一些常用的类,包,接口,请各举出5个。
Runable,ActionListener,Conllection,Map,Set,List接口
1)java.lang包----包括线程类(Thread)、异常类(Exception)、系统类(System)、整数类(Integer)和字符串类(String) 等, 这些类是java程序中经常用到的。
2)java.awt包----抽象窗口工具箱包,awt是(Abstract Window Toolkit) 的缩写。这个包中包含了用于构建GUI界面的类及绘图类。
3)java.io包----输入/输出包,包含各种输入流类和输出流类,如文件输入流类(FileInputStream类)及文件输出流类(FileOutputStream)等。
4)java.util包----提供一些实用类,如日期类(Data)和集合类(Collection)等。
5)java.net包----支持TCP/IP网络协议,包括Socket类及和URL相关的类,这些类都用于网络编程

除了上面提到的基本包,JDK中还有很多其他包,比如用于数据库编程的java.sql包,用于编写网络程序的java.rmi包(rmi是“Remote Method Invocation”的缩写)。另外,javax.*包是对基本包的扩展,包括用于编写GUI的javax.Swing包,以及用于编写声音程序的javax.sound包等。

4. 描述一下你最常用的编程风格。
1)注意编码规则,符合编码要求。
2)变量,类等起名要有意义。
3)经常格式化代码,注意格式。
4)代码中加入测试方法或测试类,尽量提早发现错误。
5)代码中要加入注释,为别人和自己将来理解代码带来方便。

5. 说一说标识符的命名规则,以及java的编程规范。

Java标识符的命名规则:
1) 标识符由字母、数字、下划线“_”、美元符号“$”或者人民币符号“¥”组成,并且首字母不能是数字。
2) 不能把关键字和保留字作为标识符。
3) 标识符没有长度限制。
4) 标识符对大小写敏感。

Java编程规范:
1)类名和接口名:首字母大写,其余字母小写。如SamDoc
2)方法名和变量名:首字母小写,其余的字母大写。
如bothEyesOfDoll。
3)包名:字母全部小写。如,com.abc.dollapp。
4)常量名:采用大写形式,单词之间以下划线“_”隔开。
如DEFAULT_COLOR_DOL。

…………………………
31、介绍JAVA中的Collection FrameWork(包括如何写自己的数据结构)?
答:Collection FrameWork如下:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)
Map提供key到value的映射

32、抽象类与接口?
答:抽象类与接口都用于抽象,但是抽象类(JAVA中)可以有自己的部分实现,而接口则完全是一个标识(同时有多重继承的功能)。
JAVA类实现序例化的方法是实现java.io.Serializable接口
Collection框架中实现比较要实现Comparable 接口和 Comparator 接口

33、STRING与STRINGBUFFER的区别。
答:STRING的长度是不可变的,STRINGBUFFER的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法

34、谈谈final, finally, finalize的区别
答:final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载
finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)
finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的

35、面向对象的特征有哪些方面
答:主要有以下四方面:
1.抽象:
抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
2.继承:
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
3.封装:
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
4. 多态性:
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。

36、String是最基本的数据类型吗
答:基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类

37、int 和 Integer 有什么区别
答:Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。原始类型封装类booleanBoolean,charCharacter,byteByte,shortShort,intInteger,
longLong,floatFloat,doubleDouble
引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关

38、运行时异常与一般异常有何异同
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

39、说出ArrayList,Vector, LinkedList的存储性能和特性
答:ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,
Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

40、HashMap和Hashtable的区别
答:HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

41、heap和stack有什么区别
答:栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。堆是栈的一个组成元素

42、Java的接口和C++的虚类的相同和不同处
答:由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。

43、Java中的异常处理机制的简单原理和应用
答:当JAVA程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是JAVA类库内置的语义检查。例如数组下标越界,会引发IndexOutOfBoundsException;访问null的对象时会引发NullPointerException。另一种情况就是JAVA允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用throw关键字引发异常。所有的异常都是java.lang.Thowable的子类。

43、垃圾回收的优点和原理。并考虑2种回收机制
答:Java语言中一个显着的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有"作用域"的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。

44、你所知道的集合类都有哪些?主要方法?
答:最常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作"键"和"值"),其中每个键映射到一个值。

45、描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

46、排序都有哪几种方法?请列举
答: 排序的方法有:插入排序(直接插入排序、希尔排序),交换排序(冒泡排序、快速排序),选择排序(直接选择排序、堆排序),归并排序,分配排序(箱排序、基数排序)
快速排序的伪代码。
/ /使用快速排序方法对a[ 0 :n- 1 ]排序
从a[ 0 :n- 1 ]中选择一个元素作为middle,该元素为支点
把余下的元素分割为两段left 和right,使得left中的元素都小于等于支点,而right 中的元素都大于等于支点
递归地使用快速排序方法对left 进行排序
递归地使用快速排序方法对right 进行排序
所得结果为left + middle + right

47、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?="3">答:Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。
用try来指定一块预防所有"异常"的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的"异常"的类型。
throw语句用来明确地抛出一个"异常"。
throws用来标明一个成员函数可能抛出的各种"异常"。
Finally为确保一段代码不管发生什么"异常"都被执行一段代码。
可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。

48、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?
答:可以。必须只有一个类名与文件名相同。

49、java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
答:字节流,字符流。字节流继承于InputStream OutputStream,字符流继承于InputStreamReader OutputStreamWriter。在java.io包中还有许多其他的流,主要是为了提高性能和使用方便。

50、java中会存在内存泄漏吗,请简单描述。
答:会。自己实现堆载的数据结构时有可能会出现内存泄露,可参看effective java.

D. 如何使用parentViewController-CSDN论坛

向后传递值,实现的方法有多种
1,可以用通知:当L2返回时发送通知 post notification,L1(订阅者)接收通知传递过来的参数,并更新content的内容。
2,delegate代理或闭包。
定义一个代理协议

Objective C code?

1
2
3

@objc protocol L2ControllerDelegate{
func returnAndUpdateContent(vc:L2Controller, content:String)
}

在L2Controller类中定义delegate的属性
weak var delegate : L2ControllerDelegate?
点击返回时回调

Objective C code?

1
2
3

@IBAction backButtonPressed(sender : AnyObject!) {
self.delegate?.returnAndUpdateContent(self, content:@"hello")
}

E. Swift:weak与unowned的奥秘

以上是 Swift 官方教程中,对 Swift 内存管理的解释。通常情况的部分很好理解,Swift 中 ARC 负责绝大部分的内存管理,ARC部分可以参考我的另一篇博客: iOS内存管理初探 – 引用计数、AutoRelease与ARC ;但少数情况下,我们需要向ARC提供对象之间的关系来使其正确管理内存。那么少数情况是什么意思呢?这得从Swift中的循环强引用讲起。

如图所示的情况中,john指向的对象强引用了unit4A指向的对象,而unit4A指向的对象又强引用了john指向的对象。

paragraph实例有一个成员asHTML强引用了一个闭包,而这个闭包中又捕获了self,意味着对self的强引用。

在ARC下,引用循环的情况是编译器无法自动解决的,这就是上文提到的少数情况。weak 和 unowned 的存才就是为了给编译器提供更多的信息,来打破循环引用。

含义 :weak 即弱引用,当把一个实例声明为弱引用时,此实例不会持有这个对象,即不会使对象的引用计数加1。当对象被废弃,其所有的弱引用会被置为 nil。

适用场景

由于 tenant 是弱引用,当 tenant 引用的对象被销毁(如赋值 nil),tenant 被置为空,并且释放对 apartment的强引用,此时 apartment 所指对象就可以正常释放了。

由于 self.delegate 指向的是外部对象,生命周期与self无关,所以可能在被捕获后变为nil。(delegate 一般都声明为weak以避免循环引用)

含义 :无主引用,与弱引用一样,当把一个实例声明为无主引用时,此实例不会持有这个对象,即不会使对象的引用计数加1。但与弱引用不同的是,当对象被废弃,其无主引用并不会被置为 nil。

适用场景

虽然在这个例子中,capitalCity 与 country 的生命周期相同,理论上讲将其中任何一个声明为无主引用都可以打破引用循环,但 capitalCity 与 country 之间有从属关系,所以倾向于将“大”的一方,即 country 声明为无主引用。

self 与属于 self 的成员变量 someClosure 生命周期相同,同时销毁,所以声明为无主引用。

Automatic Reference Counting

在Swift中,写下 unowned 相当于 unowned(safe)。但在官方文档中提到, Swift 还提供了另一种不安全的无主引用 unowned(unsafe) 来禁用运行时的安全检查。运行时的安全检查就是使 unowned(safe) 安全的原因。

unowned(safe) :当访问 unowned(safe) 类型的无主引用时,运行时会进行安全检查,如果对象已经废弃,将抛出异常并终止程序。

unowned(unsafe) :unowned(unsafe) 的作用效果实际上相当于 Objective-C 属性标示符中的 assign/unsafeunretained。访问 unowned(unsafe) 类型的无主引用时,运行时的安全检查被禁用,这时会有三种情况:

The Swift Programming Language (Swift 3.1) : Automatic Reference Counting
What is the difference in Swift between 'unowned(safe)' and 'unowned(unsafe)'?

F. 知乎上的一个怎么面试iOS工程师的问题

1.什么是arc?(arc是为了解决什么问题诞生的?)
首先解释ARC: automatic reference counting自动引用计数。
ARC几个要点:
在对象被创建时 retain count +1,在对象被release时 retain count -1.当retain count 为0 时,销毁对象。
程序中加入autoreleasepool的对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。
那么ARC是为了解决什么问题诞生的呢?这个得追溯到MRC手动内存管理时代说起。
MRC下内存管理的缺点:
1.当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
2.释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
3.模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
4.多线程操作时,不确定哪个线程最后使用完毕
2.请解释以下keywords的区别: assign vs weak, __block vs __weak
assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。
assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
而weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。
首先__block是用来修饰一个变量,这个变量就可以在block中被修改(参考block实现原理)
__block:使用__block修饰的变量在block代码快中会被retain(ARC下,MRC下不会retain)
__weak:使用__weak修饰的变量不会在block代码块中被retain
同时,在ARC下,要避免block出现循环引用 __weak typedof(self)weakSelf = self;
3.__block在arc和非arc下含义一样吗?
是不一样的。
在MRC中__block variable在block中使用是不会retain的
但是ARC中__block则是会Retain的。
取而代之的是用__weak或是__unsafe_unretained来更精确的描述weak reference的目的
其中前者只能在iOS5之后可以使用,但是比较好 (该物件release之后,此pointer会自动设成nil)
而后者是ARC的环境下为了相容4.x的解决方案。
所以上面的范例中
__block MyClass* temp = …; // MRC环境下使用
__weak MyClass* temp = …; // ARC但只支援iOS5.0以上的版本
__unsafe_retained MyClass* temp = …; //ARC且可以相容4.x以后的版本
4.使用nonatomic一定是线程安全的吗?()
不是的。
atomic原子操作,系统会为setter方法加锁。 具体使用 @synchronized(self){//code }
nonatomic不会为setter方法加锁。
atomic:线程安全,需要消耗大量系统资源来为属性加锁
nonatomic:非线程安全,适合内存较小的移动设备
5.描述一个你遇到过的retain cycle例子。
block中的循环引用:一个viewController
@property (nonatomic,strong)HttpRequestHandler * handler;
@property (nonatomic,strong)NSData *data;
_handler = [httpRequestHandler sharedManager];
[ downloadData:^(id responseData){
_data = responseData;
}];
1
2
3
4
5
6
self 拥有_handler, _handler 拥有block, block拥有self(因为使用了self的_data属性,block会 一份self)
解决方法:
__weak typedof(self)weakSelf = self
[ downloadData:^(id responseData){
weakSelf.data = responseData;
}];
1
2
3
4
6.+(void)load; +(void)initialize;有什么用处?
在Objective-C中,runtime会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。
共同点:两个方法都只会被调用一次。
7.为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)
先来看看怎么理解发送消息的含义:
曾经觉得Objc特别方便上手,面对着 Cocoa 中大量 API,只知道简单的查文档和调用。还记得初学 Objective-C 时把[receiver message]当成简单的方法调用,而无视了“发送消息”这句话的深刻含义。于是[receiver message]会被编译器转化为:
objc_msgSend(receiver, selector)
如果消息含有参数,则为:
objc_msgSend(receiver, selector, arg1, arg2, ...)
如果消息的接收者能够找到对应的selector,那么就相当于直接执行了接收者这个对象的特定方法;否则,消息要么被转发,或是临时向接收者动态添加这个selector对应的实现内容,要么就干脆玩完崩溃掉。
现在可以看出[receiver message]真的不是一个简简单单的方法调用。因为这只是在编译阶段确定了要向接收者发送message这条消息,而receive将要如何响应这条消息,那就要看运行时发生的情况来决定了。
Objective-C 的 Runtime 铸就了它动态语言的特性,这些深层次的知识虽然平时写代码用的少一些,但是却是每个 Objc 程序员需要了解的。
Objc Runtime使得C具有了面向对象能力,在程序运行时创建,检查,修改类、对象和它们的方法。可以使用runtime的一系列方法实现。

G. VC++的编译器中,运算符new底层的实现是什么

和+, -, *, /差不多,都是语言内部机制。可能是用C和汇编写的,直接操作硬件或操作系统。

回复:
晕,上面的五级的经理,看你级别挺高,怎么不给个像样的答案呢?说的跟算命先生似的。什么也没有。。

确实是说得简单了。
不同编译器的实现是不同的,现在给一个实现,只有一部分,其他的自己看gcc的源代码。
#include <bits/c++config.h>
#include <cstdlib>
#include <exception_defines.h>
#include "new"

using std::new_handler;
using std::bad_alloc;
#if _GLIBCXX_HOSTED
using std::malloc;
#else
extern "C" void *malloc (std::size_t);
#endif

extern new_handler __new_handler;

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) throw (std::bad_alloc)
{
void *p;

/* malloc (0) is unpredictable; avoid it. */
if (sz == 0)
sz = 1;
p = (void *) malloc (sz);
while (p == 0)
{
new_handler handler = __new_handler;
if (! handler)
#ifdef __EXCEPTIONS
throw bad_alloc();
#else
std::abort();
#endif
handler ();
p = (void *) malloc (sz);
}

return p;
}

H. Object C 属性、特性、类型

@property声明属性,做了三件事

.h: 声明了getter和setter方法;

.h: 声明了实例变量(默认:下划线+属性名);

.m: 实现了getter和setter方法。

是否给setter和getter加锁,是否保证setter或者getter的每次访问是完整性的

atomic (默认值)

一定程度上可以保证线程安全,有线程在访问setter,其他线程只能等待完成后才能访问。

nonatomic

不保证你获得的是有效值

readwrite,就是告诉编译器,同时生成getter和setter。如果是readonly,只生成getter。

readwrite: “读写”
readonly:  “只读”

strong (默认值)

表明你需要引用(持有)这个对象,负责保持这个对象的生命周期。

基本数据类型(非对象类型,如int, float, BOOL),默认值并不是strong,strong只能用于对象类型。

weak

跟strong相反,属性所指的对象销毁时,属性值也会清空,设置为nil。

会给你一个引用,指向对象。但是不会主张所有权。也不会增加retain count。

在delegate patterns中常用weak解决strong reference cycles(以前叫retain cycles)问题。



会在赋值前,复制一个对象,指向新对象

NSString,NSArray,NSDictonary,推荐使用属性

NSMubtableString,NSMutableArray, NSMutableDictonary属性则使用strong属性。

assign

针对基本数据类型赋值操作。

nullable :对象“可为空”

nonnull :对象“不可为空”

null_unspecified :“未指定”

null_resettable :调用setter去reset属性时,可以传入nil,但是getter返回值,不为空。

① 四种整型 :

short int : 短整型, 占16位, mac 上占 2 字节, ios 上占 2 字节, 范围 -32768(-2^15) ~ 32767(2^15 - 1), 3万;

int : 整型, 占32位, mac 上占 4 字节, ios 上占 4 字节, 范围 -2147483648(-2^31) ~ 2147483647(2^31 - 1), 21亿;

long int : 长整型, 占64位, mac 上占 8 字节, ios 上占 4 字节, (-2^63) ~ (2^63 - 1), 922亿亿;

long long : 占64位, mac 上占 8 字节, ios 上占 8 字节;

② 进制 :

八进制 十六进制赋值 : 八进制由 "0" 开头, 十六进制由 "0x" 或者 "0X" 开头;

③ 无符号整型:
无符号整型 第一位 不是符号位, 范围比原来要大,例 unsigned short int 范围是 0到6万

--  %d  : 十进制整数, 正数无符号, 负数有 "-" 符号;

--  %o  : 八进制无符号整数, 没有 0 前缀;

--  %x  : 十六进制无符号整数, 没有 0x 前缀;

--  %u  : 十进制无符号整数;

单个字符表示 : 使用 '' 将单个字符引起来, 便是字符常量, 如 'a', 'b' 等;

转义字符表示 : 使用转义符号 \ 来指定字符, 如 '\n' 等;

字符占用空间大小 : 每个字符占用一个字节, 因此 Object-C 不支持中文字符, 因为中文字符都是占 2 ~ 3 个字节;

--  %c  : 单个字符输出;

--  %s  : 输出字符串;

float  : 占 4 字节;

double  : 占 8 字节;

long double  : 占 16 字节;

CGFloat :对于需要兼容64位机器的程序而言,需要用CGFloat,当然从长远角度考虑还是推荐尽量使用CGFloat。尽管在32位上相比float增加了一些memory footprint的消耗

(Object-C浮点数 : 不区分 double 与 float, 一个浮点数 3.8 可以赋值给两种类型的变量)

--  %f  : 以小数形式输出浮点数, 默认 6 位小数;

--  %e  : 以指数形式输出浮点数, 默认 6 位小数;

--  %g  : 自动选择 %e 或者 %f 各式;

① 定义普通枚举:

定义方式 : 格式 enum enum_name {elem1, elem2, elem3 ...};

示例 : enum day{Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};

定义枚举变量 : 格式 enum enum_name var1, var2;

示例 : enum day today, tomorrow, 注意 today tomorrow 两个变量的取值只能是 day 枚举中定义的值;

枚举变量赋值 : 格式 variable = elm1 ;

示例 : today = Sunday; tomorrow = Friday;

② 定义匿名枚举:

匿名枚举格式 : enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday} today, tomorrow ;

说明 : 两个枚举变量 today 和 tomorrow 只能取值 enum 中得枚举值;

③ 枚举值简介

枚举值常量 : 在 {} 中得是枚举常量 或者 枚举元素, 该元素不是变量, 不能对齐进行赋值, 枚举的值按照定义的顺序 0, 1, 2, 3 以此类推;

枚举值本质 : 枚举值属于无符号整数, 可以使用 %u 占位符打印出来, 其值也能进行大小比较, 和四则运算;

枚举初值 : 枚举值可以在定义的时候赋予一个初值;

--  %p  : 输出十六进制形式的指针地址;

--  %@  : 输出 Object-C 对象;

BOOL 类型值  : 该类型至右两个值 YES 和 NO ;

BOOL 类型本质  : 该类型本质是 signed char, YES 是 1, NO 是 0, 在处理的时候 YES 会被当成真处理, NO 会被当成假处理;

nil相当于Java中的null,表示一个对象,这个对象的指针指向空。Nil是定义一个指向空的类而不是对象。

可以存放任何数据类型的对象,类似Java中的Object类,其被定义为指向对象的指针(本身就是指针了),故定义比如id instance = nil;

id类型是多态和动态绑定的基础。

Object C 数字、字符串、集合、字典、NSURL、NSDate

下一章: Object C 数字、字符串、集合、字典、NSURL、NSDate - (jianshu.com)

I. c++ if .……else 大家帮帮看看什么问题,谢谢!

编的没问题

解决出现fatal error LNK1169: one or more multiply(转)

大家都知道,从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,然后由汇编器 (assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;(2)链接器(linker)将一个个的目标文件(或许还会有若干程序库)链接在一起生成一个完整的可执行文件。

编译器编译源文件时会把源文件的全局符号(global symbol)分成强(strong)和弱(weak)两类传给汇编器,而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢?编译器认为函数与初始化了的全局变量都是强符号,而未初始化的全局变量则成了弱符号。比如有这么个源文件:

extern int errorno;
int buf[2] = {1,2};
int *p;

int main()
{
return 0;
}

其中main、buf是强符号,p是弱符号,而errorno则非强非弱,因为它只是个外部变量的使用声明。

有了强弱符号的概念,我们就可以看看链接器是如何处理与选择被多次定义过的全局符号:

规则1: 不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);

规则2: 如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;

规则3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;

由上可知多个目标文件不能重复定义同名的函数与初始化了的全局变量,否则必然导致LNK2005和LNK1169两种链接错误。可是,有的时候我们并没有在自己的程序中发现这样的重定义现象,却也遇到了此种链接错误,这又是何解?嗯,问题稍微有点儿复杂,容我慢慢道来。

众所周知,ANSI C/C++ 定义了相当多的标准函数,而它们又分布在许多不同的目标文件中,如果直接以目标文件的形式提供给程序员使用的话,就需要他们确切地知道哪个函数存在于哪个目标文件中,并且在链接时显式地指定目标文件名才能成功地生成可执行文件,显然这是一个巨大的负担。所以C语言提供了一种将多个目标文件打包成一个文件的机制,这就是静态程序库(static library)。开发者在链接时只需指定程序库的文件名,链接器就会自动到程序库中寻找那些应用程序确实用到的目标模块,并把(且只把)它们从库中拷贝出来参与构建可执行文件。几乎所有的C/C++开发系统都会把标准函数打包成标准库提供给开发者使用(有不这么做的吗?)。

程序库为开发者带来了方便,但同时也是某些混乱的根源。我们来看看链接器是如何解析(resolve)对程序库的引用的。

在符号解析(symbol resolution)阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:(1)集合E是将被合并到一起组成可执行文件的所有目标文件集合;(2)集合U是未解析符号(unresolved symbols,比如已经被引用但是还未被定义的符号)的集合;(3)集合D是所有之前已被加入到E的目标文件定义的符号集合。一开始,E、U、D都是空的。

(1): 对命令行中的每一个输入文件f,链接器确定它是目标文件还是库文件,如果它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,然后处理下一个输入文件。

(2): 如果f是一个库文件,链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号,那么就把 m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。

(3): 如果处理过程中往D加入一个已存在的符号,或者当扫描完所有输入文件时U非空,链接器报错并停止动作。否则,它把E中的所有目标文件合并在一起生成可执行文件。

VC带的编译器名字叫cl.exe,它有这么几个与标准程序库有关的选项: /ML、/MLd、/MT、/MTd、/MD、/MDd。这些选项告诉编译器应用程序想使用什么版本的C标准程序库。/ML(缺省选项)对应单线程静态版的标准程序库(libc.lib);/MT对应多线程静态版标准库(libcmt.lib),此时编译器会自动定义_MT宏;/MD对应多线程DLL版 (导入库msvcrt.lib,DLL是msvcrt.dll),编译器自动定义_MT和_DLL两个宏。后面加d的选项都会让编译器自动多定义一个 _DEBUG宏,表示要使用对应标准库的调试版,因此/MLd对应调试版单线程静态标准库(libcd.lib),/MTd对应调试版多线程静态标准库 (libcmtd.lib),/MDd对应调试版多线程DLL标准库(导入库msvcrtd.lib,DLL是msvcrtd.dll)。虽然我们的确在编译时明白无误地告诉了编译器应用程序希望使用什么版本的标准库,可是当编译器干完了活,轮到链接器开工时它又如何得知一个个目标文件到底在思念谁?为了传递相思,我们的编译器就干了点秘密的勾当。在cl编译出的目标文件中会有一个专门的区域(关心这个区域到底在文件中什么地方的朋友可以参考COFF和 PE文件格式)存放一些指导链接器如何工作的信息,其中有一种就叫缺省库(default library),这些信息指定了一个或多个库文件名,告诉链接器在扫描的时候也把它们加入到输入文件列表中(当然顺序位于在命令行中被指定的输入文件之后)。说到这里,我们先来做个小实验。写个顶顶简单的程序,然后保存为main.c :

/* main.c */
int main() { return 0; }

用下面这个命令编译main.c(什么?你从不用命令行来编译程序?这个......) :

cl /c main.c

/c是告诉cl只编译源文件,不用链接。因为/ML是缺省选项,所以上述命令也相当于: cl /c /ML main.c 。如果没什么问题的话(要出了问题才是活见鬼!当然除非你的环境变量没有设置好,这时你应该去VC的bin目录下找到vcvars32.bat文件然后运行它。),当前目录下会出现一个main.obj文件,这就是我们可爱的目标文件。随便用一个文本编辑器打开它(是的,文本编辑器,大胆地去做别害怕),搜索"defaultlib"字符串,通常你就会看到这样的东西: "-defaultlib:LIBC -defaultlib:OLDNAMES"。啊哈,没错,这就
是保存在目标文件中的缺省库信息。我们的目标文件显然指定了两个缺省库,一个是单线程静态版标准库libc.lib(这与/ML选项相符),另外一个是oldnames.lib(它是为了兼容微软以前的C/C++开发系统)。

VC的链接器是link.exe,因为main.obj保存了缺省库信息,所以可以用

link main.obj libc.lib

或者

link main.obj

来生成可执行文件main.exe,这两个命令是等价的。但是如果你用

link main.obj libcd.lib

的话,链接器会给出一个警告: "warning LNK4098: defaultlib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library",因为你显式指定的标准库版本与目标文件的缺省值不一致。通常来说,应该保证链接器合并的所有目标文件指定的缺省标准库版本一致,否则编译器一定会给出上面的警告,而LNK2005和LNK1169链接错误则有时会出现有时不会。那么这个有时到底是什么时候?呵呵,别着急,下面的一切正是为喜欢追根究底的你准备的。

建一个源文件,就叫mylib.c,内容如下:

/* mylib.c */
#i nclude

void foo()
{
printf("%s","I am from mylib!\n");
}



cl /c /MLd mylib.c

命令编译,注意/MLd选项是指定libcd.lib为默认标准库。lib.exe是VC自带的用于将目标文件打包成程序库的命令,所以我们可以用

lib /OUT:my.lib mylib.obj

将mylib.obj打包成库,输出的库文件名是my.lib。接下来把main.c改成:

/* main.c */
void foo();

int main()
{
foo();
return 0;
}



cl /c main.c

编译,然后用

link main.obj my.lib

进行链接。这个命令能够成功地生成main.exe而不会产生LNK2005和LNK1169链接错误,你仅仅是得到了一条警告信息:"warning LNK4098: defaultlib "LIBCD" conflicts with use of other libs; use /NODEFAULTLIB:library"。我们根据前文所述的扫描规则来分析一下链接器此时做了些啥。

一开始E、U、D都是空集,链接器首先扫描到main.obj,把它加入E集合,同时把未解析的foo加入U,把main加入D,而且因为 main.obj的默认标准库是libc.lib,所以它被加入到当前输入文件列表的末尾。接着扫描my.lib,因为这是个库,所以会拿当前U中的所有符号(当然现在就一个foo)与my.lib中的所有目标模块(当然也只有一个mylib.obj)依次匹配,看是否有模块定义了U中的符号。结果 mylib.obj确实定义了foo,于是它被加入到E,foo从U转移到D,mylib.obj引用的printf加入到U,同样地,mylib.obj指定的默认标准库是libcd.lib,它也被加到当前输入文件列表的末尾(在libc.lib的后面)。不断地在my.lib库的各模块上进行迭代以匹配U中的符号,直到U、D都不再变化。很明显,现在就已经到达了这么一个不动点,所以接着扫描下一个输入文件,就是 libc.lib。链接器发现libc.lib里的printf.obj里定义有printf,于是printf从U移到D,而printf.obj被加入到E,它定义的所有符号加入到D,它里头的未解析符号加入到U。链接器还会把每个程序都要用到的一些初始化操作所在的目标模块(比如crt0.obj 等)及它们所引用的模块(比如malloc.obj、free.obj等)自动加入到E中,并更新U和D以反应这个变化。事实上,标准库各目标模块里的未解析符号都可以在库内其它模块中找到定义,因此当链接器处理完libc.lib时,U一定是空的。最后处理libcd.lib,因为此时U已经为空,所以链接器会抛弃它里面的所有目标模块从而结束扫描,然后合并E中的目标模块并输出可执行文件。

上文描述了虽然各目标模块指定了不同版本的缺省标准库但仍然链接成功的例子,接下来你将目睹因为这种不严谨而导致的悲惨失败。

修改mylib.c成这个样子:

#i nclude

void foo()
{
// just a test , don"t care memory leak
_malloc_dbg( 1, _NORMAL_BLOCK, __FILE__, __LINE__ );
}

其中_malloc_dbg不是ANSI C的标准库函数,它是VC标准库提供的malloc的调试版,与相关函数配套能帮助开发者抓各种内存错误。使用它一定要定义_DEBUG宏,否则预处理器会把它自动转为malloc。继续用

cl /c /MLd mylib.c
lib /OUT:my.lib mylib.obj

编译打包。当再次用

link main.obj my.lib

进行链接时,我们看到了什么?天哪,一堆的LNK2005加上个贵为"fatal error"的LNK1169垫底,当然还少不了那个LNK4098。链接器是不是疯了?不,你冤枉可怜的链接器了,我拍胸脯保证它可是一直在尽心尽责地照章办事。

一开始E、U、D为空,链接器扫描main.obj,把它加入E,把foo加入U,把main加入D,把libc.lib加入到当前输入文件列表的末尾。接着扫描my.lib,foo从U转移到D,_malloc_dbg加入到U,libcd.lib加到当前输入文件列表的尾部。然后扫描 libc.lib,这时会发现libc.lib里任何一个目标模块都没有定义_malloc_dbg(它只在调试版的标准库中存在),所以不会有任何一个模块因为_malloc_dbg而加入E,但是每个程序都要用到的初始化模块(如crt0.obj等)及它们所引用的模块(比如malloc.obj、 free.obj等)还是会自动加入到E中,同时U和D被更新以反应这个变化。当链接器处理完libc.lib时,U只剩_malloc_dbg这一个符号。最后处理libcd.lib,发现dbgheap.obj定义了_malloc_dbg,于是dbgheap.obj加入到E,它里头的未解析符号加入U,它定义的所有其它符号也加入D,这时灾难便来了。之前malloc等符号已经在D中(随着libc.lib里的malloc.obj加入E而加入的),而dbgheap.obj又定义了包括malloc在内的许多同名符号,这引发了重定义冲突,链接器只好中断工作并报告错误。

现在我们该知道,链接器完全没有责任,责任在我们自己的身上。是我们粗心地把缺省标准库版本不一致的目标文件(main.obj)与程序库 (my.lib)链接起来,导致了大灾难。解决办法很简单,要么用/MLd选项来重编译main.c;要么用/ML选项重编译mylib.c。

在上述例子中,我们拥有库my.lib的源代码(mylib.c),所以可以用不同的选项重新编译这些源代码并再次打包。可如果使用的是第三方的库,它并没有提供源代码,那么我们就只有改变自己程序的编译选项来适应这些库了。但是如何知道库中目标模块指定的默认库呢?其实VC提供的一个小工具便可以完成任务,这就是mpbin.exe。运行下面这个命令

mpbin /DIRECTIVES my.lib

然后在输出中找那些"Linker Directives"引导的信息,你一定会发现每一处这样的信息都会包含若干个类似"-defaultlib:XXXX"这样的字符串,其中XXXX便代表目标模块指定的缺省库名。

知道了第三方库指定的默认标准库,再用合适的选项编译我们的应用程序,就可以避免LNK2005和LNK1169链接错误。喜欢IDE的朋友,你一样可以到 "Project属性" -> "C/C++" -> "代码生成(code generation)" -> "运行时库(run-time library)" 项下设置应用程序的默认标准库版本,这与命令行选项的效果是一样的。

终极解决办法:

在 Project/Setting/Link/General中的 Project Options: 加入 /FORCE:MULTIPLE即可

J. map中的key为结构体时,怎么find

第一个问题是关于 map 的。话不多说,以下 20 多行的 C++ 代码重现了我遇到的问题:
#include <iostream>
#include <map>
using namespace std;

struct S {
int x, y;
S(int xx, int yy): x(xx), y(yy) {}
bool operator <(const S& s) const {
return x < s.x && y < s.y;
}
};

map<S, int > ms;

int main() {
ms.insert(map<S, int >::value_type(S(31, 41), 59));

S test(31, 59);
if (ms.find(test) != ms.end()) {
cout << "Find the value: " << ms[test] << endl;
} else {
cout << "Find Failure/n" ;
}
return 0;
}
使用 VC++6.0 , VC++2005 Express Edition, VC++2005 command line compiler( 不带任何编译选项 ) , g++ 测试的结果都相同,最后输出:

Find the value: 59
这个问题比较隐蔽。多个编译器测试结果相同说明肯定不是编译器版本相关的问题。直接调试进入 find 函数就可以明白:
iterator find(const key_type& _Keyval)
{ // find an element in mutable sequence that matches _Keyval
iterator _Where = lower_bound(_Keyval);
return (_Where == end()
|| _DEBUG_LT_PRED(this ->comp,
_Keyval, _Key(_Where._Mynode()))
? end() : _Where);
}
虽然这样调试会遇到一些 STL 内部的细节,但整体实现思路还是可看出来。在 find 函数中, lower_bound 返回值是结点 (31, 41) 。跟踪进入,发现调用的 _DEBUG_LT_PRED 的定义如下:
#define _DEBUG_LT_PRED(pred, x, y) _Debug_lt_pred(pred, x, y, __FILEW__, __LINE__)

template <class _Pr, class _Ty1, class _Ty2> inline
bool __CLRCALL_OR_CDECL _Debug_lt_pred(_Pr _Pred, const _Ty1& _Left, const _Ty2& _Right,
const wchar_t *_Where, unsigned int _Line)
{ // test if _Pred(_Left, _Right) and _Pred is strict weak ordering
if (!_Pred(_Left, _Right))
return (false );
else if (_Pred(_Right, _Left))
_DEBUG_ERROR2("invalid operator<" , _Where, _Line);
return (true );
}
( 注:关于 _Debug_lt_pred 函数有三个重载版本,分别是针对参数 _Left, _Right 的 const 性质的,看这些代码能学到很多东西。另外,如果静态地看这些代码来分析自己程序中的错误,则因为有大量的重载函数,所以静态分析时很难自己确定到底哪一个函数被调用,而动态调试就能一步到位。 )
从这个函数的代码里大致就能看出问题所在了。猜测这里的 _Pred 参数就是自己在 struct 里定义的那个 operator < ,编译器中看到 _Pred 的 value 是 {lessthan } , type 是 std::less <S> ,但这里有更大的发现: strict weak ordering!!! 自己 C++ 功底很浅,这是一个新的发现,马上 google 一下 ”strict weak ordering” 这个关键词,果然发现大量的专题链接!暂且先放下这个主题。问题猜测肯定是出在 operator < 这个函数上了,因为根据自己的 operator < 定义: {31, 41} < {31, 59} 返回值是 false , {31, 59} < {31, 41} 的返回值也是 false ,那么,由这两个比较能得出结论: {31, 41} == {31, 59} !!! 这也无怪乎程序运行会返回不期望的结果了。
但这也只是猜测,继续调试,看能不能找到 _Pred 函数的真实面目。上面说了从编译器中看出 _Pred 的 type 是 std::less <S> ,在 MSDN 中找到 less 是 STL 中的一个模板类,以下是在 MSDN 中看到的定义:

less
less
template<class T>

struct less
: public binary_function
<T, T, bool> {

bool operator()
(const T& x, const T& y) const;

};
The template class defines its member function as returning x < y . The member function defines a total ordering , even if T is an object pointer type.

我们接着调试,跟踪进入 _Pred 函数,发现它的定义如下:
template <class _Ty>
struct less
: public binary_function<_Ty, _Ty, bool >
{ // functor for operator<
bool operator ()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator< to operands
return (_Left < _Right);
}
};
它最终比较 _Left 和 _Right 时调用的正是 struct S 中定义的 operator < 。
至此,问题真相大白。还遗留两个主题:一个是关于 strict weak ordering ,另一个是 STL 中的一些实现方法,因为以上只是跟踪调试过程把沿途看到的东西机械地记录了下来,并不是真正的理解。

无独有偶,今天遇到另一个问题,关于 STL 中的 sort 函数的问题,这个问题是唯独在 VC++ 2005 Express Edition 中才出现的,并且在命令行下使用 cl.exe 不带任何选项编译连接时正常,使用 g++ 也正常。问题的表现就是程序在运行时出现异常,信息是: ”invalid operator <” 。这个问题就不再重现调试了,它的解决方法见下列地址:
http://support.microsoft.com/kb/949171

strict weak ordering 是一个数学上的术语,刚刚给出的这个地址上面有关于 strict weak ordering 的简明的解释,贴过来:

The STL algorithms for stable_sort ( ) and sort() require the binary predicate to be strict weak ordering.

For example:

· Strict: pred (X, X) is always false.

· Weak: If ! pred (X, Y) && !pred (Y, X), X==Y.

· Ordering: If pred (X, Y) && pred (Y, Z), then pred (X, Z).

阅读全文

与Vc编译器weak属性相关的资料

热点内容
电脑感染exe文件夹 浏览:914
wpsppt怎么转pdf格式 浏览:86
腾讯文档在线编辑怎么添加密码 浏览:868
本地不能访问服务器地址 浏览:865
访问服务器命令 浏览:835
华为云服务器分销商 浏览:954
Linux定位内存泄露 浏览:198
工程加密狗视频 浏览:720
不在内网怎么连接服务器 浏览:664
云服务器app安卓下载 浏览:966
如何查看linux服务器的核心数 浏览:137
交易平台小程序源码下载 浏览:148
程序员记笔记用什么app免费的 浏览:646
java与单片机 浏览:897
服务器内网如何通过公网映射 浏览:478
程序员穿越到宋代 浏览:624
怎么使用云服务器挂游戏 浏览:618
真实的幸福pdf 浏览:345
d盘php调用c盘的mysql 浏览:266
怎么样搭建源码网站 浏览:430