热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS开发者应该掌握些C++知识

作为一名iOS开发者,最近由于开发音频播放器需要一些C++的知识,便开始学习C++知识。看完2本C++的书后,对它有一些了解,这里分享给有需要的同学,如果写的有不妥的地方,还望指出

作为一名 iOS 开发者,最近由于开发音频播放器需要一些 C++ 的知识,便开始学习 C++ 知识。看完2本C++的书后,对它有一些了解,这里分享给有需要的同学,如果写的有不妥的地方,还望指出。后续会利用学到的C++知识对 FreeStreamer 这个音频播放器写一篇分析其实现原理的文章。本文主要围绕 FreeStreamer 这个库用到的 C++ 语法来讲解。

iOS 开发当中,偶尔会使用到 C++ 的知识,然而大多数同学每遇到这个问题时,选择逃避。如果从头开始学习C++语法,会花费很多时间,如同笔者一样,花费很多时间了解基础的知识。

Objective-C 和 C++ 都是基于 C 语言而设计的,它们都具有面向对象的能力。在学习 C++ 知识之前,我们先了解下 Objective-C++,它可以把 C++ 和 Objective-C 结合起来使用,如果把两门语言的优点结合起来使用是不是会更好?腾讯开源的数据库 WCDB 是一个很好的例子,它不仅有 C++ 而且还有 Objective-C++。

本文主要内容:

  • 类(Class)的定义与使用
  • 命名空间
  • 内存管理
  • 继承
  • 构造函数和析构函数
  • virtual 关键字
  • 静态成员与静态函数
  • 运算符重载
  • 打印日志
  • 实例

类(Class)

类在面向对象中非常重要,在 Objective-C 中,所有的 Class 必需继承自 NSObject 类,当创建一个类的时候,它会包含一个 .h 和 一个 .m 文件,我们以定义一个 Person 类为例:

/* Person.h */
#import
@interface Person : NSObject
@end
/* Person.m */
#import "Person.h"
@implementation Person
@end

而 C++ 中,创建一个类也会有一个头文件 Person.hpp 和实现文件 Person.cpp

/* Person.hpp */
#include
class Person {};
/* Person.cpp */
#include "Person.hpp"

从上面的例子我们可以看到,OC 和 C++ 定义类主要有以下不同:

  • 在实现文件中,C++ 中没有 @implementation Person @end;
  • OC 中每个类需要继承自 NSObject;
  • C++ 中使用 #include 导入其它文件中的代码,而 OC 使用 #import 导入其它文件代码,使用 #import 保证每个文件中不会重复导入,而使用 #include 需要开发者保证不要重复导入。

class Person {
public:
int age;
bool isChild();
private:
float height;
bool isNomalHeight();
};

Person 类中定义了一个公有的成员变量 age 和 一个成员函数 isChild。一个私有的成员变量 height 和一个成员函数 isNomalHeight 。在 C++ 中可以定义某个变量或函数的作用范围,如果使用时超出作用范围编译器将会报错。而在 OC 中既使在 .h 文件中没有定义某个函数,我们任然可以调用,所以在 OC 中经常会出现以 _ 或某个前缀开头的私有函数。

Person 类的实现:

bool Person::isChild(){
return age >= 18;
}
bool Person::isNomalHeight(){
return height >= 170;
}

其中 :: 表示 isChild 函数属于 Person 类,它告诉编译器从哪里找到函数 isChild。

定义了一个 Person 类,使用的时候和 OC 会有一些不同。

OC中只能创建堆上的对象

Person *aPerson = [[Person alloc] init];
aPerson.age = 18;

在栈上创建一个 Person

Person aPerson;
aPerson.age = 18;
NSLog(@"age == %@", @(aPerson.age));
2018-02-19 12:12:20.252108+0800 C++Demo[2480:84138] age == 18

在堆上创建一个 Person

Person *aPerson = new Person();
aPerson->age = 18;
NSLog(@"aPerson age == %@", @(aPerson->age));
delete aPerson;
2018-02-19 12:16:25.432998+0800 C++Demo[2525:88221] aPerson age == 18

命名空间

在 C++ 中有命名空间的概念,可以帮助开发者在开发新的软件组件时不会与已有的软件组件产生命名冲突,而在 OC 中却没有命名空间的概念,我们常以前缀来与第三方库区分。它的定义为:

namespace 命名空间的名字 { }

我们将上面定义的类加上命名空间:

namespace Lefex {
class Person {
public:
int age;
bool isChild();
private:
float height;
bool isNomalHeight();
};
}
namespace Lefex {
bool Person::isChild(){
return age >= 18;
} bool Person::isNomalHeight(){
return height >= 170;
}
}

那么使用时必须加上命名空间:

Lefex::Person aPerson;
aPerson.age = 18;
NSLog(@"age == %@", @(aPerson.age));

内存管理

在 OC 中使用引用计数来管理内存,当引用计数为 0 时,内存空间将被释放。而 C++ 中需要开发者自己管理内存。理解 C++ 的内存管理,我们有必要先了解一下栈内存和堆内存。

  • 栈内存:它分配的大小是固定的,当一个函数执行时,将为某些变量分配存储空间,当函数执行完成后将释放其对应的存储空间。
  • 堆内存:它会随着应用的运行,使用的空间逐步增加,分配的存储空间需要开发者自己释放。

void Person::ageMemory(){
int stackAge = 20;
int *heapAge = (int *)malloc(sizeof(int));
*heapAge = 20;
free(heapAge);
}

stackAge 为栈内存,不需要开发者自己释放内存,当 ageMemory 函数执行完成后 stackAge 将被释放。heapAge 为堆空间,当函数 ageMemory 执行完成后,它不会释放,需要开发者手动释放。

下面这个例子是创建了一个 Person 对象,它使用的是堆内存,需要使用 delete 释放其内存空间。这里需要注意访问堆对象时使用 -> 访问它的成员或者方法,而访问栈对象时使用 . 访问它的成员或者方法。

在 OC 中,当一个对象为 nil 时调用一个方法时 [nil doSomeThing] , 程序并不会执行 doSomeThing 方法,而在 C++ 中,NULL-> doSomeThing ,程序将 crash。

Lefex::Person *aPerson = new Lefex::Person();
aPerson->age = 18;
NSLog(@"age == %@", @(aPerson->age));
NSLog(@"is a child: %@", @(aPerson->isChild()));
delete aPerson;

继承

C++ 中支持多继承,也就是说一个类可以继承多个类。而在 OC 中只能使用单继承。与 OC 中不同的一点就是增加了修饰符(比如:public),这样用来限制继承的属性和方法的范围。

// 男人
class Man: public Lefex::Person {};
// 女人
class Woman: public Lefex::Person {};
// 人妖
class Freak: public Man, public Woman {
};

构造函数和析构函数

构造函数通常在 OC 中使用的是 init,而在 C++ 中默认的构造函数是于类名相同的函数。比如类 Person 的默认构造函数是 Person(),自定义一个构造函数 Person(int age, int height) 。在 OC 中析构函数如 dealloc,而在 C++ 中是函数 ~Person(),当一个类被释放后,析构函数会自动执行。

// 默认构造函数
Person::Person(){
printf("Init person");
}
// 初始化列表构造函数
Person::Person()
:age(0),
height(0),
m_delegate(0){
printf("Init person\n");
}
// 自定义构造函数
Person::Person(int age, int height){
this->age = age;
this->height = height;
}
// 析构函数
Person::~Person(){
printf("Dealloc person");
}

虚析构函数:为了保证析构函数可以正常的被执行,引入了虚析构函数,一般基类中的析构函数都是虚析构函数。定义方式如下。

virtual ~Person();
Person::~Person(){
printf("person dealloc called \n");
}

virtual 关键字

虚函数是一种非静态的成员函数,定义格式:

virtual <类型说明符> <函数名> { 函数体 }

纯虚函数:是一种特殊的虚函数,这是一种没有具体实现的虚函数,定义格式:

virtual <类型说明符> <函数名> (<参数表>)=0;

抽象类:它是一个不能有实例的类,这样的类唯一用途在于被继承。一个抽象类中至少具有一个虚函数,它主要的作用来组织一个继承的层次结构,并由它提供一个公共的根。

有了抽象类和纯虚函数,就可以实现类似于 OC 中的 delegate。

// 相当于 OC 中的代理
class Person_delegate {
public:
// =0 表示无实现
virtual void ageDidChange()=0;
};
// 继承了 Person_delegate,Woman 类真正实现了 Person_delegate 的纯虚函数
class Woman: public Lefex::Person, public Lefex::Person_delegate {
public:
Woman();
void ageDidChange();
};

综上可以看到它于 OC 中实现的思路一致。

静态成员与静态函数

静态成员使用 static 修饰,只对其进行一次赋值,将一直保留这个值,直到下次被修改。

在 Person 类中定义一个静态变量 weight。

static int weight;

使用时直接:Person::weight; 即可访问静态变量。

静态成员函数与静态成员的定义一致。

static float currentWeight();

需要注意的是,在静态成员函数中,不可以使用非静态成员。

调用:

Person::currentWeight();
也可以:
aPerson->currentWeight();

运算符重载

有时候利用系统的运算符作自定义对象之间的比较的时候,不得不对运算符进行重载。

定义:

类型 operator op(参数列表) {}

“类型”为函数的返回类型,函数名是 “operator op”,由关键字 operator 和被重载的运算符op组成。“参数列表”列出该运算符所需要的操作数。

例子:

// Person 类中定义 Person 是否相等。
bool operator==(Person &){
if (this->age == person.age) {
return true;
}
return false;
};

+ (void)operatorOverload
{
Lefex::Person aPerson;
aPerson.age = 18; Lefex::Person aPerson2;
aPerson2.age = 18; if (aPerson == aPerson2) {
NSLog(@"aPerson is equal to aPerson2");
} else {
NSLog(@"aPerson is not equal to aPerson2");
}
}

打印日志

C++ 中打印日志需要导入库 #include

  • \n 和 endl 效果一样都是换行;
  • 打印多个使用 <<拼接;

void Woman::consoleLog(){
std::cout<<"Hello Lefe_x\n";
std::cout<<"Hello " <<"Lefe_x\n";
int age = 20;
std::cout<<"Lefe_x age is " <}

打印结果:

Hello Lefe_x
Hello Lefe_x
Lefe_x age is 20

实例

下面这段代码摘自 FreeStreamer 中的 audio_queue.h,有部分重复的知识点有删减。建议读者仔细看看下面的代码,看看有没有看不懂的地方。


namespace astreamer {
class Audio_Queue_Delegate;class Audio_Queue {
public:
Audio_Queue_Delegate *m_delegate;
Audio_Queue();
virtual ~Audio_Queue(); void start();
private:
void cleanup();
static void audioQueueOutputCallback(void *inClientData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
};
class Audio_Queue_Delegate {
public:
virtual void audioQueueStateChanged(Audio_Queue::State state) = 0;
virtual void audioQueueFinishedPlayingPacket() = 0;
};
} // namespace astreamer

总结

总的来说,这些内容是最基本的语法知识,希望可以帮你入门 C++。

===== 我是有底线的 ======

喜欢我的文章,欢迎关注我的新浪微博 Lefe_x,我会不定期的分享一些开发技巧


推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 近期,某用户在重启RAC一个节点的数据库实例时,发现启动速度非常慢。同时业务部门反馈连接RAC存活节点的业务也受影响。通过对日志的分析, ... [详细]
  • Python中程序员的面试题有哪些
    小编给大家分享一下Python中程序员的面试题有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有 ... [详细]
  • http:oj.leetcode.comproblemsminimum-depth-of-binary-tree贡献了一次runtimeerror,因为如果输入为{}即空的时候,出 ... [详细]
  • pdf在这里:http:mfinocchiaro.files.wordpress.com200807java-virtual-machine-neutral.pdf以及关于gc的 ... [详细]
  • 一条数据的漫游 XEngine SIGMOD Paper Introduction
    大多数人追寻永恒的家园(归宿),少数人追寻永恒的航向。----瓦尔特.本雅明背景X-Engine是阿里数据库产品事业部自研的OLTP数据库存储引擎, ... [详细]
  • iOS安全攻防(二十四):敏感逻辑的保护方案(1)Objective-C代码容易被hook,暴露信息太赤裸裸,为了安全,改用C来写吧!当然不是全部代码都要C来写,我指的是敏感业务逻 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 本文介绍了sqlserver云存储和本地存储的区别,云存储是将数据存储在网络上,方便查看和调用;本地存储是将数据存储在电脑磁盘上,只能在存储的电脑上查看。同时提供了几种启动sqlserver的方法。此外,还介绍了如何导出数据库的步骤和工具。 ... [详细]
  • 本文介绍了自动化测试专家Elfriede Dustin在2008年的文章中讨论了自动化测试项目失败的原因。同时,引用了IDT在2007年进行的一次软件自动化测试的研究调查结果,调查显示很多公司认为自动化测试很有用,但很少有公司成功实施。调查结果表明,缺乏资源是导致自动化测试失败的主要原因,其中37%的人认为缺乏时间。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 4.3.2Tuple是否可以跨页面PostgreSQLusesafixedpagesize(commonly8kB),anddoesnotallowtuplestospanmult ... [详细]
  • 牛B三人组快速排序堆排序归并排序
    快速排序随便取个数,作为标志值,这里就默认为索引位置为0的值记录左索引和右索引,从右往左找比标志值小的,小值和左索引值交换& ... [详细]
author-avatar
小哥脾气
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有