c++編程
c++
c++是c的超集,因此這裏你將會學到更多的技術。和Objective-C相比,c++有不少不一樣的概念和語法,儘管他們都是面向對象的編程語言。他們有不少重要的不一樣之處:類,指針,多重繼承,內存管理,和模板。sql
class類數據庫
這點和Objective-C相似,一個類有兩個文件:一個是頭文件,另外一個是實現文件。你一樣能夠指定private和public的成員或方法。下面是聲明一個類的頭文件的標準方式:編程
class Cat { 編程語言
public:
ide
// public accessors
unsigned int GetAge();
void SetAge(unsigned int Age);函數
Cat ();
Cat (int initialAge);~Cat();性能
// public member functions學習
void Meow();ui
// private member data
private:
unsigned int itsAge;
};
// GetAge, Public accessor function
// returns value of itsAge member
unsigned int Cat::GetAge() {
return itsAge;
}
// returns sets itsAge member
void Cat::SetAge(unsigned int age) {
// set member variable its age to
// value passed in by parameter age
itsAge = age;
}
// action: Prints "meow" to screen
void Cat::Meow() {
cout << "Meow.\n";
}
int main() {
Cat cat;
cat.Meow();
}
你能夠看到,public和private聲明能夠分開放在不一樣地方,就像Objective-C中定義頭文件時使用的修飾符@private和@public。對於類的實現,你須要在方法的前面加上類的名稱,來代表方法是屬於這個類的。
對於訪問和設置方法,你須要顯示的聲明他們:
unsigned int GetAge();
void SetAge(unsigned int Age);
爲了建立一個Cat對象,你只須要聲明Cat cat;而後調用方法cat.Meow();可是對象只存在函數的局部範圍內部。
對於內存管理,你須要同時聲明構造函數和析構函數。若是沒有聲明你本身的構造函數,編譯器會使用一個沒有參數的默認構造函數。
注意:若是聲明瞭本身的構造函數,請記住也要聲明析構函數,即便它沒有作什麼事情。遵照這個預約是很是好的。若是使用Cat cat建立Cat的一個新對象,你須要一個默認構造函數。 |
指針和內存管理
若是你想要在程序其餘地方使用你新建立的對象,在c++中,你須要在自由存儲區中申請分配內存。這個對象會一直存在,直到你顯示的在內存中把它刪除,就像c語言同樣。爲了聲明指向一個包含整型值的內存地址指針,你能夠像這樣:
int *pInt;
pInt = new int;
*pInt = 72;
分配和釋放一個對象,你可使用new和delete關鍵字。
Cat *pCat = new Cat;
pCat->SetAge(5);
delete pCat;
pCat = 0;
注意:若是你delete同一個指針兩次,你的應用會崩潰。若是你在從新分配以前沒有delete它,會有內存泄露。 |
繼承
相比於Objective-C,c++繼承的概念更加複雜。你能夠像正常同樣使用繼承,複寫,多態。惟一不一樣的是virtual和pure virtual方法。
class Dog : Mammal {
}
Dog類繼承了Mammal類的全部屬性和方法,同時它可以有本身的屬性和方法。當Dog對象被建立的時候,Mammal的構造函數先調用,而後在調用Dog的構造函數。
c++繼承中有一個很是重要的概念:你只可以複寫virtual方法。對於其餘方法,你只能繼承而不能複寫。在父類和子類中能夠有兩個同名的方法,可是不存在複寫和多態。對於virtual方法,建議在必要的時候在子類中進行復寫。c++中方法默認的修飾符是非virtual的。有兩種類型的虛函數:虛函數和純虛函數。
#include "stdafx.h"
#include "stdio.h"
using namespace System;
class Animal {
public:
virtual void Speak() = 0;
virtual void Eat();
void Run();
};
void Animal::Eat(){
printf("Animal eats\n");
}
void Animal::Run(){
printf("Animal runs\n");
}
class Dog : Animal{
public:
void Eat();
void Speak();
void Run();
};
void Dog::Eat(){
printf("Dog eats\n");
}
void Dog::Run(){
printf("Dog runs\n");
}
void Dog::Speak(){
printf("Dog speaks\n");
}
int main(){
Animal *dog1 = (Animal *) new Dog;
dog1->Run();
dog1->Speak();
dog1->Eat();
getchar();
}
對於虛函數,在父類中你能夠有它的實現;可是對於純虛函數,你只能聲明。Eat方法是虛函數,而Speak是純虛函數。在子類中,這兩個方法都須要複寫。
由於Run不是虛函數,子類不須要複寫。所以main函數的輸出將是
Animal Runs
Dog Speaks
Dog Eats
多重繼承
在c++中,你的類能夠繼承不少父類,所以你可以重用更多的屬性和方法。例如,Teacher類同時繼承Employee和Person類,如如9-4。
你能夠經過下面這樣進行多重繼承
class Teacher : public Person, public Employee {
}
多重繼承存在一個廣泛的問題:當兩個父類有相同的方法時。例如,Pesron和Employee兩個類都聲明瞭GetIncome方法。所以,當你在Teacher對象中調用此方法時,你必須顯示的指定是調用哪一個父類的方法。
若是你只是這樣寫
int currentIncome = pTeacherGetIncome();
你會獲得一個編譯錯誤
Member is ambiguous: ‘Person::GetIncome’ and ‘Employee::GetIncome’
你須要顯示的指明調用哪一個父類的函數來消除這個二義性。
int currentIncome = pTeacherEmployee::GetIncome();
模板
在c++中,你能夠定義類或方法只接收某種指定的類型。例如,你能夠定義一個方法,只接收兩個同類型的參數進行比較而後返回結果。
template <class T>
T GetMax (T a, T b) {
return (a>b)? a : b;
}
int main () {
int i=5, j=6, k;
long l=10, m=5, n;
k=GetMax<int>(i,j);
n=GetMax<long>(l,m);
return 0;
}
經過定義一個模板方法,你能夠確保a和b是一樣的數據類型,這樣他們就能夠進行比較。你能夠在類中也進行一樣的操做。
template <class T>
class mypair {
T a, b;
public:
mypair (T first, T second){
a=first;
b=second;
}
T getmax () {
return a>b? a : b;
}
};
int main () {
mypair <int> myobject (100, 75);
cout << myobject.getmax();
return 0;
}
一個實際例子
我將經過一個實際的例子來引導你如何將c/c++庫集成到Objective-C代碼中。一個例子是集成SQLite庫,另外一個例子是講c++和Objective-C代碼進行混編。
SQLite
SQLite是CoreData的底層實現。SQLite性能仍是不錯的,由於比較輕量級和更加直接。可是,CoreData在這些方法提供了更好的支持:模型對象匹配,undo和redo功能,批量操做,線程。
使用SQLite的最大好處是:若是你的團隊已經很熟悉相關的數據庫和SQL,學習的曲線也是很是輕量級的。SQLite一樣很容易移植到Android中,這樣比就能夠在iPhone和Android應用共享相同的數據了。
你能夠閱讀SQLiteSample工程的源代碼。這裏我只給你一些重要的提示。
你須要打開命令行,而後書輸入sqlite3 students.sql 來建立數據庫。這會建立一個新的數據庫,而後就能夠在這個數據庫中建立表,插入和查詢數據。你可使用SQL語句建立表和插入數據。
sqlite> CREATE TABLE students (pk INTEGER PRIMARY KEY, name VARCHAR(25));
sqlite> INSERT INTO students (name) VALUES ('khang');
sqlite> INSERT INTO students (name) VALUES ('vo');
sqlite> INSERT INTO students (name) VALUES ('duy');
sqlite> .quit
圖9-5更加清楚的展現了在命令行中發生的事情。
你能夠在Home文件夾(這個是默認的文件夾,若是你在終端操做時沒有修改的話)下找到students.sql文件。你須要把這個文件添加到Xcode中,而後要添加libsqlite3.0.dylib這個庫。
- (void)viewDidAppear:(BOOL)animated {
NSString *path = [[NSBundle mainBundle] pathForResource:@"students" ofType:@"sql"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
// Get the primary key for all books.
const char *sql = "SELECT pk, name FROM students";
sqlite3_stmt *statement;
// Preparing a statement compiles the SQL query into a byte-code program in theSQLite library.
// The third parameter is either the length of the SQL string or -1 to read up tothe first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// We "step" through the results - once for each row.
while (sqlite3_step(statement) == SQLITE_ROW) {
const unsigned char *name = sqlite3_column_text(statement, 1);
NSString *nameString = [NSString stringWithCString:(char *)nameencoding:NSASCIIStringEncoding];
}
}
// "Finalize" the statement - releases the resources associated with thestatement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, @"Failed to open database with message '%s'.",
sqlite3_errmsg(database));
}
[self.tableView reloadData];
}
這裏有一些方法你須要記住:
sqlite3_open([path UTF8String], &database) == SQLITE_OK 初始化和將新的數據庫對象賦值給你的指針。
sqlite3_prepare_v2(database, sql, -1, &statement, NULL) ==SQLITE_OK 執行sql命令和把結果放入statement中。
while (sqlite3_step(statement) == SQLITE_ROW): 從執行的statement中遍歷結果。
在你的應用中集成c++
你能夠絕不費力的將c集成到你的應用中,由於Objective-C是c的超集。可是,當你在應用中集成c++的時候,你須要注意一些細節。
首先,你須要像正常同樣的建立c++類的頭文件和實現文件(.h和.cpp文件)。而後,你須要把包裹c++文件的Objective-C文件的後綴改成.mm(在iOS環境中這些是Objective-C++文件)。任何想要使用.mm文件的文件也必須像a.mm這樣。
你能夠查看一下TestC_CPlus這個示例工程。首先,你會看到兩個文件,Foo_Cpp.h 和Foo_Cpp.cpp,他們聲明瞭Foo_Cpp這個c++類。而後,經過MyObject.h和MyObject.m這兩個Objective-c++文件進行包裹。
最後一步,把這些邏輯集成到你的view controller中。你能夠在工程中像正常同樣調用MyObject對象。你只須要將把調用的文件後綴改成.mm。
總結
在編程領域中,已經有不少高性能的和由特殊任務的c/c++庫。你永遠不須要重複發明輪子,或努力的將c/c++代碼轉換成Objective-c代碼。理解c/c++可以幫助你更好的理解庫的使用,這樣你就可以把他們集成到你的應用中。
Objective-C是c的超集,因此不少c的特性你都已經知道了。我討論了c編程的一些主要的點,包括指針的概念。你也學到了函數指針,位操做,這些都是c的特性,可以很好的提高性能。
若是你沒有編程經驗的話,c++比較難以學習和理解。我僅僅討論了c++一些主要的知識點,c++和Objective-C的主要不一樣之處。對於編寫一個高要求的c++程序可能信息還不夠,可是對於如何將一個已經存在的c++庫集成到iPhone應用中已經足夠了。
在最後一部分,我使用了一個SQLite的真實的例子展現瞭如何在Objective-C中使用C。這個並不難,除了一些語法和內存管理上的不一樣。對於c++庫我並無深刻討論;我只是展現瞭如何將c++代碼集成到你的iPhone應用中。