大家好,我在工作經(jīng)常發(fā)現(xiàn)小伙伴們遇到一些C++的問(wèn)題都是對(duì)基礎(chǔ)知識(shí)不熟悉或理解混亂所導(dǎo)致的。正所謂萬(wàn)丈高樓平地起,作為一名合格的程序員來(lái)說(shuō),沒(méi)有良好的基本功很難達(dá)到一定的高度。而工作中大部分編程問(wèn)題都是基本功不扎實(shí)所導(dǎo)致,所以決定花些時(shí)間來(lái)整理C++相關(guān)的基本知識(shí)和基本概念供大家參考理解,每一個(gè)知識(shí)點(diǎn)都結(jié)合相關(guān)的代碼進(jìn)行驗(yàn)證。本文基本上涵蓋了C++最常用的知識(shí)點(diǎn),希望對(duì)小伙伴們有所幫助。
1. C++是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言
- C++支持?jǐn)?shù)據(jù)封裝,支持?jǐn)?shù)據(jù)封裝就是支持?jǐn)?shù)據(jù)抽象。在C++中,類(lèi)是支持?jǐn)?shù)據(jù)封裝的工具,對(duì)象則是數(shù)據(jù)封裝的實(shí)現(xiàn)。面向過(guò)程的程序設(shè)計(jì)方法與面向?qū)ο蟮某绦蛟O(shè)計(jì)方法在對(duì)待數(shù)據(jù)和函數(shù)關(guān)系上是不同的。在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,將數(shù)據(jù)和對(duì)該數(shù)據(jù)進(jìn)行合法操作的函數(shù)封裝在一起作為一個(gè)類(lèi)的定義,數(shù)據(jù)將被隱藏在封裝體中,該封裝體通過(guò)操作接口與外界交換信息。對(duì)象被說(shuō)明具有一個(gè)給定類(lèi)的變量,
類(lèi)
類(lèi)似于C語(yǔ)言中的結(jié)構(gòu),在C語(yǔ)言中可以定義結(jié)構(gòu),但這種結(jié)構(gòu)中包含數(shù)據(jù),而不包含函數(shù)。C++中的類(lèi)是數(shù)據(jù)和函數(shù)的封裝體。在C++中,結(jié)構(gòu)可作為一種特殊的類(lèi),它雖然可以包含函數(shù),但是它沒(méi)有私有或受保護(hù)的成員。 - C++類(lèi)中包含
私有
、公有
和受保護(hù)成員
,C++類(lèi)中可定義三種不同訪(fǎng)控制權(quán)限的成員。一種是私有(Private)成員,只有在類(lèi)中說(shuō)明的函數(shù)才能訪(fǎng)問(wèn)該類(lèi)的私有成員,而在該類(lèi)外的函數(shù)不可以訪(fǎng)問(wèn)私有成員;另一種是公有(Public)成員,類(lèi)外面也可訪(fǎng)問(wèn)公有成員,成為該類(lèi)的接口;還有一種是保護(hù) (Protected)成員,這種成員只有該類(lèi)的派生類(lèi)可以訪(fǎng)問(wèn),其余的在這個(gè)類(lèi)外不能訪(fǎng)問(wèn)。
2. 命名空間
- c++所有標(biāo)志符都在std命名空間作用域中才可見(jiàn)
using std::cout;
using std::endl; 或 using namepace std;
using std::cin;
- 命名空間中可以嵌套定義命名空間
namespace name_3
{
namespace
{
int k = 200;
};
};
- 命名空間取別名
namespace name = name_3;
3. C++中的struct結(jié)構(gòu)體
- 對(duì)比C語(yǔ)言中結(jié)構(gòu)體,C++中結(jié)構(gòu)體不僅可以有變量還可以有函數(shù)。
例程中聲明一個(gè)命名空間Test,Test中聲明一個(gè)結(jié)構(gòu)體Account,而Account中定義變量和聲明函數(shù)。
namespace Test
{
struct Account
{
char name[30];
double balance;
void init(char *myname,double mybalance)
{
strcpy(name,myname);
balance = mybalance;
}
void deposit(double amount);
void withdraw(double amount);
inline double getBalance()
{
return balance;
}
};
}
4. Const屬性修改
在c中const的意思是“一個(gè)不能被改變的普通變量“,在c中它總是占用存儲(chǔ)空間而且它的名字是全局符。
c++編譯器通常并不為const分配空間,它把這個(gè)定義保存在符號(hào)表中。當(dāng)const常量被使用時(shí),編譯的時(shí)候就進(jìn)行常量折疊
。 c++中 編譯器不會(huì)為一般的const常量分配內(nèi)存空間, 而是將它們存放符號(hào)表中。如果取了這個(gè)常量的地址,那么編譯器將為此常量分配一個(gè)內(nèi)存空間,生成一個(gè)常量副本, 所有通過(guò)地址對(duì)常量的操作都是針對(duì)副本。 常量折疊,又叫常量替換,c++編譯器會(huì)在編譯時(shí),將const常量的字面值保存在符號(hào)表中,在編譯時(shí)使用這個(gè)字面常量進(jìn)行替換。
const_cast用法:const_cast<type_id> (expression) 該運(yùn)算符用來(lái)修改類(lèi)型的const或volatile屬性。除了const或volatile修飾之外,要求type_id和expression的類(lèi)型是一樣的。 常量指針被轉(zhuǎn)化成非常量的指針,并且仍然指向原來(lái)的對(duì)象; 常量引用被轉(zhuǎn)換成非常量的引用,并且仍然指向原來(lái)的對(duì)象;
#include <iostream>
using namespace std;
int main(int argc, const char** argv)
{
const int a = 10;
const int *p = &a;
// (*p) = 11; // error 不可修改
std::cout << "a:" << a << "*p:" << *p << std::endl; // a:10 *p:10
const_cast<int&>(*p) = 11; // 強(qiáng)制修改
std::cout << "a:" << a << "*p:" << *p << std::endl; // a:10 *p:11
return 0;
}
5. 引用和指針
- 引用是一個(gè)別名,可以把它看作變量本身,但是指針本身也是一個(gè)變量
- 引用在定義的時(shí)候必須初始化,必須綁定一個(gè)對(duì)象,如果一個(gè)對(duì)象本身不存在則取別名也沒(méi)有意義。所以對(duì)指針進(jìn)行解引用(*)的時(shí)候要對(duì)指針進(jìn)行非空檢測(cè),但是引用由于定義的時(shí)候肯定初始化了,則一定不為空。
- 非const引用不能綁定到const對(duì)象,但是const引用可以綁定到非const對(duì)象(對(duì)象本身可以修改自己,但是不能通過(guò)引用修改對(duì)象)
- 引用比指針安全,引用只能綁定到一個(gè)對(duì)象,指針可以指向多個(gè)地方,可能會(huì)造成內(nèi)存溢出或懸掛指針等不安全的因素
- 形參傳引用效率高,但引用為形參最好是一個(gè)const引用,防止實(shí)參本身被修改。
#include <iostream>
using namespace std;
int main(void)
{
int a = 10;
// int &ref; // error! 必須初始化
// int &ref = 10; // error!
// const int &ref = 10; // ok
const int &ref_a = a; // ok
// ref_a = 11;
a = 12;
cout <<"ref_a: "<<a<< endl;
/* const int b = 20;
int &ref_b = b; // error! 非const引用不能綁定到const變量
const int &res_b = b; // ok
int c = 11;
const int &ref_c = c; // ok
//ref_c++; // error!
*/
return 0;
}
6. 內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù):用inline關(guān)鍵字修飾的函數(shù),不加inline默認(rèn)叫外聯(lián)。
- 使用了inline關(guān)鍵字,編譯器并不一定把此函數(shù)當(dāng)作內(nèi)聯(lián)函數(shù)處理 內(nèi)聯(lián)函數(shù)代碼一般1-5行,并且函數(shù)體中不能出現(xiàn)循環(huán)或遞歸。
- 內(nèi)聯(lián)函數(shù)的聲明和定義是在一起的
- 在類(lèi)中聲明和定義在一起的成員函數(shù)都默認(rèn)為內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)和宏定義 宏定義:在預(yù)處理階段替換,但是容易產(chǎn)生二義性,不能作為類(lèi)的成員函數(shù)訪(fǎng)問(wèn)私有成員。 內(nèi)聯(lián)函數(shù):在編譯階段函數(shù)代碼替換函數(shù)名,在調(diào)用運(yùn)行的時(shí)候就沒(méi)有函數(shù)的壓棧和出棧的操作,提高運(yùn)行效率,是空間換時(shí)間。 如果要用某函數(shù)的指針調(diào)用它,則該函數(shù)不能是內(nèi)聯(lián)函數(shù),如果動(dòng)態(tài)鏈接庫(kù)中有內(nèi)聯(lián)函數(shù),那么該鏈接庫(kù)升級(jí)的時(shí)候需要重新編譯。內(nèi)聯(lián)函數(shù)和宏定義都不支持調(diào)試。
#include <iostream>
using namespace std;
#define max(a,b) (a>b?a:b)
int max1(int a,int b)
{
return a>b?a:b;
}
inline int max2(int a,int b)
{
return a>b?a:b;
}
int main(void)
{
cout <<"#define: "<<max(11,12)<<endl; //預(yù)處理階段替換
cout <<"max1: "<<max1(12,11)<<endl; //函數(shù)的壓棧出棧
cout <<"max2: "<<max2(12,11)<<endl; //運(yùn)行時(shí)內(nèi)聯(lián)的展開(kāi)
return 0;
}
7. 重載
- 什么是函數(shù)重載: 1.函數(shù)名相同 2.參數(shù)列表必須不同(參數(shù)類(lèi)型,參數(shù)個(gè)數(shù),參數(shù)的順序不同) 3.跟函數(shù)返回值沒(méi)有關(guān)系
#include <iostream>
using namespace std;
/*
*默認(rèn)參數(shù):如果某個(gè)參數(shù)被默認(rèn)初始化了,其右邊不能出現(xiàn)沒(méi)有被默認(rèn)初始化的參數(shù)
* Error:
* int average(double a=0.5, double b =1.1, double c)
* {
* return (a+b+c)/2;
* }
*/
int average(double a=0.5, double b =1.1, double c=2.1)
{
return (a+b+c)/2;
}
int average(int a, int b)
{
cout <<"average(int, int)" << endl;
return (a+b)/2;
}
int average(double a, double b)
{
cout <<"average(double, double)" << endl;
return (a+b)/2;
}
int average(double a, double b, double c)
{
cout <<"average(double,double,double)" << endl;
return (a+b+c)/2;
}
int main(void)
{
int a = 10, b = 11;
average(a, b);
cout <<"--------------" << endl;
double c = 10.1, d = 10.2, e = 10.3;
average(c, d);
cout <<"--------------" << endl;
average(c, d, e);
return 0;
}
8. const成員函數(shù)和const對(duì)象
- const成員函數(shù)(常成員函數(shù)) 不修改成員變量的函數(shù)我們一般定義為const屬性的成員函數(shù),常成員函數(shù)不能修改成員變量的值。
- const對(duì)象 (常對(duì)象) const屬性的對(duì)象(如:const Person p),常對(duì)象所有的成員變量都是const屬性,不能用常對(duì)象調(diào)用非const的成員函數(shù)(常對(duì)象只能調(diào)用常成員函數(shù))
#include <iostream>
using namespace std;
class Person{
public:
Person(){}
~Person(){}
void set(int var)
{
this->m_var = var;
}
int get() const //常成員函數(shù)
{
// m_var2 = 12; //error! 不修改成員變量
return m_var;
}
private:
int m_var;
int m_var2;
};
int main(void)
{
const Person p; // 常對(duì)象
//p.set(11); // error! const對(duì)象不能修改非const成員函數(shù)
const_cast<Person &>(p).set(11); //ok 只對(duì)當(dāng)前生效
cout <<"const_cast: "<< p.get() <<endl;
//p.set(12); //error! 同樣是不對(duì)的
return 0;
}
9. static成員
- static:靜態(tài)成員:靜態(tài)成員變量 和 靜態(tài)成員函數(shù)
- 靜態(tài)成員屬于整個(gè)類(lèi)不屬于某一個(gè)對(duì)象,在靜態(tài)存儲(chǔ)區(qū)分配內(nèi)存空間,被所有實(shí)例對(duì)象共享,如果是公共的(public)則可以在外部加類(lèi)作用域直接訪(fǎng)問(wèn)。
- 靜態(tài)成員變量只能被初始化一次,不要在頭文件中定義,為了避免重復(fù)定義。不要在構(gòu)造函數(shù)中定義(構(gòu)造函數(shù)可能被調(diào)用多次)另外是因?yàn)轭?lèi)的聲明不分配內(nèi)存空間.靜態(tài)成員變量的初始化方式:int Person::m_var = 10;靜態(tài)成員變量只在靜態(tài)存儲(chǔ)區(qū)保留一份拷貝,靜態(tài)成員變量可以聲明為本來(lái)的類(lèi)類(lèi)型
- 靜態(tài)成員函數(shù)沒(méi)有this指針,不能訪(fǎng)問(wèn)普通成員變量。非靜態(tài)成員函數(shù)即可以訪(fǎng)問(wèn)普通成員變量也可以訪(fǎng)問(wèn)靜態(tài)成員變量
#include <iostream>
using namespace std;
class Person{
public:
Person(){
cout <<"constructor" << endl;
m_counter++;
}
~Person(){
cout <<"distructor" << endl;
m_counter--;
}
static void out_counter(){
//cout <<m_var<< endl; // error 不能訪(fǎng)問(wèn)普通成員變量
cout <<"people counter: "<<m_counter << endl; //ok
}
// private:
int m_var;
static int m_counter;
};
int Person::m_counter = 0; // 靜態(tài)成員變量初始化
int main(void)
{
Person p1; //constructor
Person p2; //constructor
Person p3; //constructor
// p1.out_counter();
// p3.out_counter();
Person *p4 = new Person; //constructor
cout <<"people counter: "<<Person::m_counter << endl; // people counter:4
delete p4; //distructor
cout <<"people counter: "<<Person::m_counter << endl; // people counter:3
return 0;
}
10. 面向?qū)ο?/span>
- OOP(Object Oriented Programming) 特征:封裝、繼承、多態(tài)、抽象 private和public針對(duì)類(lèi)本身和調(diào)用者 struct的缺省作用域是public class的缺省作用域是private
person.h
namespace mystd
{
class Person
{
private:
char name[30];
int age;
bool gender;
public:
Person(char *myname, int age, bool gender);
~Person();
void show(); // 顯示基本屬性信息
int afterYear(int n); // n年后多少歲
inline int getAge()
{
return age;
}
};
};
person.cpp
#include "person.h"
#include <iostream>
#include <string.h>
using namespace std;
namespace mystd
{
Person::Person(char *myname, int myage, bool mygender)
{
strcpy(name, myname);
age = myage;
gender = mygender;
}
Person::~Person(){}
void Person::show() // 顯示基本屬性信息
{
cout <<"name: "<<name<<" age: "<<age<<" gender: "<<gender << endl;
}
int Person::afterYear(int n)// n年后多少歲
{
return age += n;
}
};
main.cpp
#include "person.h"
#include <iostream>
using namespace std;
using namespace mystd;
int main(void)
{
Person p1("Lin", 24, true);
p1.show();
cout <<"after ten years: " << p1.afterYear(10)<<endl;
cout <<p1.age<< endl;
return 0;
}
11. 構(gòu)造函數(shù)和析構(gòu)函數(shù)
- 構(gòu)造函數(shù) 1.特殊的成員函數(shù),名字跟類(lèi)名相同,沒(méi)有返回類(lèi)型,必須為public,構(gòu)造函數(shù)的作用是初始化對(duì)象的屬性。 2.如果沒(méi)有顯示的定義任何構(gòu)造函數(shù),則編譯器會(huì)自動(dòng)合成一個(gè)構(gòu)造函數(shù),如果定義類(lèi)構(gòu)造函數(shù),則編譯器不再自己合成構(gòu)造函數(shù)。 3.構(gòu)造函數(shù)可以重載 4.構(gòu)造函數(shù)初始化可以使用初始化參數(shù)列表,成員變量的初始化順序跟初始化列表的順序無(wú)關(guān),是按照成員變量的聲明順序。 5.什么時(shí)候一定要用初始化列表 有const成員變量和引用成員變量的時(shí)候一定要用初始化列表初始化這兩種變量
- 析構(gòu)函數(shù) 沒(méi)有返回值,析構(gòu)函數(shù)名稱(chēng)類(lèi)型前加~ 1.如果是棧對(duì)象 作用域結(jié)束的時(shí)候自動(dòng)調(diào)用析構(gòu)函數(shù) 2.如果是堆對(duì)象(new出來(lái)的對(duì)象),則要程序員delete的時(shí)候才調(diào)用析構(gòu)函數(shù) 3.如果是靜態(tài)對(duì)象(在靜態(tài)存儲(chǔ)區(qū)/靜態(tài)數(shù)據(jù)區(qū)中的對(duì)象)則在程序裝入內(nèi)存的時(shí)候就構(gòu)造,進(jìn)程結(jié)束的時(shí)候析析構(gòu)。
- 淺拷貝、深拷貝 當(dāng)由一個(gè)已有的對(duì)象來(lái)構(gòu)造一個(gè)新的對(duì)象時(shí),需要調(diào)用拷貝構(gòu)造函數(shù) 淺拷貝(位拷貝):對(duì)象成員變量沒(méi)有使用動(dòng)態(tài)分配內(nèi)存空間的時(shí)候,對(duì)象和對(duì)象之間進(jìn)行拷貝構(gòu)造的時(shí)候使用淺拷貝就行 深拷貝:如果對(duì)象內(nèi)存使用了動(dòng)態(tài)分配內(nèi)存空間,則要使用深拷貝(顯示定義進(jìn)行內(nèi)存拷貝的拷貝構(gòu)造函數(shù)) 如果不進(jìn)行深拷貝,則會(huì)造成懸掛指針,多次析構(gòu)同一塊堆內(nèi)存
- 什么時(shí)候會(huì)調(diào)用拷貝構(gòu)造函數(shù): 1.把一個(gè)對(duì)象作為參數(shù)進(jìn)行值傳遞的時(shí)候(所以說(shuō)我們一般把對(duì)象作為參數(shù)的時(shí)候傳對(duì)象的const引用) 2.把一個(gè)對(duì)象作為返回值的時(shí)候 如果存在拷貝構(gòu)造函數(shù)的需求,沒(méi)有顯示的定義拷貝構(gòu)造函數(shù),則編譯器會(huì)自動(dòng)生成一個(gè)拷貝構(gòu)造函數(shù)。如果顯示定義了拷貝構(gòu)造函數(shù)之后,則會(huì)調(diào)用該顯示定義的拷貝構(gòu)造函數(shù),但是編譯器還是會(huì)自動(dòng)生成一個(gè)拷貝構(gòu)造函數(shù)。新創(chuàng)建的對(duì)象去調(diào)用拷貝構(gòu)造函數(shù)。
constructor.cpp
#include <iostream>
using namespace std;
class Person
{
public:
Person(){cout <<"---constructor---"<< endl;}
// Person(int a){m_var1 = a;}
Person(int a):m_var1(a),m_var2(m_var2),m_var3(a)
{
// m_var3 = 3; //error
cout <<"Person(int, int) constructor" << endl;
}
~Person(){cout <<"---distructor---" << endl;}
void show(){cout <<"m_var1: "<<m_var1 <<" m_var2: "<<m_var2<< endl;}
private:
int m_var1;
int m_var2;
const int m_var3;
// int &ref;
};
Person p1(10);
int main()
{
// Person p(10, 30);
// p.show();
cout <<"----------------" << endl;
Person *p = new Person(10);
p->show();
delete p;
p1.show();
cout <<"----------------" << endl;
return 0;
}
copy_constructor.cpp
#include <iostream>
#include <string>
using namespace std;
class Computer{
public:
Computer(){}
Computer(Computer&){
cout <<"----computer constructor----" << endl;
}
~Computer(){}
private:
string name;
};
class Student{
public:
Student(){}
Student(string name, int age){
cout <<"----constructor----" << endl;
Computer com;
m_comp = com;
this->name = name;
this->age = age;
}
//拷貝構(gòu)造函數(shù)
Student(const Student &s){
cout <<"----copy constructor----" << endl;
this->name = s.name;
this->age = s.age;
}
~Student(){}
//把一個(gè)對(duì)象作為返回值的時(shí)候會(huì)調(diào)用該對(duì)象的一個(gè)拷貝構(gòu)造函數(shù)
Computer getComp(){
cout <<"getComp()"<< endl;
return m_comp;
}
Student getst(){
return *this; //會(huì)調(diào)用該對(duì)象的拷貝構(gòu)造函數(shù)
}
void disp(){
cout <<"name: "<<name<<" age: "<<age << endl;
}
private:
string name;
int age;
Computer m_comp;
};
//把對(duì)象作為參數(shù)進(jìn)行值傳遞的時(shí)候拷貝構(gòu)造函數(shù)會(huì)被調(diào)用
void disp_out(Student s)
{
s.disp();
}
int main(void)
{
Student s1("Lin", 20);
s1.disp();
Student s2 = s1; // 自動(dòng)調(diào)用拷貝構(gòu)造函數(shù)
s2.disp();
Student s3(s2); // 顯式使用拷貝構(gòu)造函數(shù)
s3.disp();
cout <<"******************" << endl;
disp_out(s3); // 把一個(gè)對(duì)象作為值傳遞的時(shí)候會(huì)調(diào)用拷貝構(gòu)造函數(shù)
cout <<"------------------" << endl;
s1.getComp();
s3.getst();
return 0;
}
deep_copy.cpp
#include <iostream>
#include <string.h>
using namespace std;
class Mystring{
public:
Mystring(char *str, int counter){
m_str = new char[counter+1];
memcpy(m_str, str, counter);
m_counter = counter;
m_str[counter+1] = '\0';
}
//顯示定義拷貝構(gòu)造函數(shù) 進(jìn)行深拷貝(也就是進(jìn)行內(nèi)存的拷貝)
Mystring(const Mystring &str){
// m_str = str.m_str; // error!
cout <<"deep copy constructor" << endl;
this->m_str = new char[str.m_counter+1];
memcpy(m_str, str.m_str, str.m_counter);
this->m_counter = str.m_counter;
m_str[m_counter+1] = '\0';
}
~Mystring(){
cout <<"distructor" << endl;
delete []m_str;
}
void disp(){
cout <<hex<<m_str<< endl;
}
private:
char *m_str;
int m_counter;
};
int main(void)
{
Mystring str("briupemsd1109", 13);
str.disp();
Mystring str1 = str; //用一個(gè)已有的對(duì)象去構(gòu)造一個(gè)新的對(duì)象
str1.disp();
return 0;
}
12. this指針
- 類(lèi)的任何對(duì)象的成員函數(shù)都會(huì)有一個(gè)隱含的this指針,也就是該類(lèi)對(duì)象的一個(gè)指針。如果要返回一個(gè)對(duì)象的引用則可以返回 *this表示對(duì)象本身。
address.h
#ifndef _ADDRESS_H_
#define _ADDRESS_H_
#include <string>
#include <iostream>
using namespace std;
class Address{
public:
Address(){}
Address(string country, string province, string city, string street)
{
this->country = country;
this->province = province;
this->city = city;
this->street = street;
}
~Address(){}
void setProvince(string province){
this->province = province;
}
string getProvince() const{
return province;
}
void setStreet(string street){
this->street = street;
}
string getStreet() const{
return street;
}
void out(){
cout <<country<<"-"<<province<<"-"<<city<<"-"<<street<<endl;
}
private:
string country;
string province;
string city;
string street;
};
#endif
player.h
#ifndef _PLAYER_H_
#define _PLAYER_H_
#include "address.h"
#include <iostream>
using namespace std;
class Player{
public:
Player(){addr = NULL;}
Player(int num, string name, int age, double salary)
{
this->num = num;
this->name = name;
this->age = age;
this->salary = salary;
this->addr = NULL;
}
Player(int num, string name, int age, double salary, Address *addr){
this->num = num;
this->name = name;
this->age = age;
this->salary = salary;
this->addr = addr;
}
~Player(){}
void setNum(int num){
this->num = num;
}
int getNum() const{
return num;
}
void setAge(int age){
this->age = age;
}
int getAge() const{
return age;
}
void setSalary(double salary){
this->salary = salary;
}
double getSalary() const{
return salary;
}
void setAddress(Address *addr){
this->addr = addr;
}
Address *getAddress(){
return addr;
}
void out(){
cout <<"num: "<<num<< endl;
cout <<"name: "<<name<< endl;
cout <<"age: " <<age<<endl;
cout <<"salary: "<<salary <<endl;
if(addr != NULL){
addr->out();
}
cout <<"******************" << endl;
}
private:
int num;
string name;
int age;
double salary;
Address *addr;
};
#endif
player_test.cpp
#include "address.h"
#include "player.h"
#include <iostream>
using namespace std;
int main(void)
{
Address addr("china", "shanghai", "shanghai","huaihai RD");
Player p1(17, "Lin", 24, 10000, &addr);
p1.out();
cout <<"-------------------" << endl;
Player p2(24, "Kobe", 34, 20000);
Address addr2("china", "jiangsu", "kunshan","xuey RD");
p2.setAddress(&addr2);
p2.out();
cout <<"-------------------" << endl;
Address *addr3 = new Address("USA", "MAIAMI", "MAIAMI", "MM.RD");
Player *p3 = new Player(6, "James", 28, 20000, addr3);
p3->out();
delete addr3;
delete p3;
return 0;
}
13. 友元
類(lèi)具備封裝和信息隱藏的特性。只有類(lèi)的成員函數(shù)才能訪(fǎng)問(wèn)類(lèi)的私有成員,程序中的其他函數(shù)是無(wú)法訪(fǎng)問(wèn)私有成員的。非成員函數(shù)能夠訪(fǎng)問(wèn)類(lèi)中的公有成員,但是假如將數(shù)據(jù)成員都定義為公有的,這又破壞了隱藏的特性。另外,應(yīng)該看到在某些情況下,特別是在對(duì)某些成員函數(shù)多次調(diào)用時(shí),由于參數(shù)傳遞,類(lèi)型檢查和安全性檢查等都需要時(shí)間開(kāi)銷(xiāo),而影響程序的運(yùn)行效率。 為了解決上述問(wèn)題,提出一種使用友元的方案。友元是一種定義在類(lèi)外部的普通函數(shù),但他需要在類(lèi)體內(nèi)進(jìn)行說(shuō)明,為了和該類(lèi)的成員函數(shù)加以區(qū)別,在說(shuō)明時(shí)前面加以關(guān)鍵字friend。友元不是成員函數(shù),但是他能夠訪(fǎng)問(wèn)類(lèi)中的私有成員。友元的作用在于提高程序的運(yùn)行效率,但是,他破壞了類(lèi)的封裝性和隱藏性,使得非成員函數(shù)能夠訪(fǎng)問(wèn)類(lèi)的私有成員。
- 友元包括:友元函數(shù)、友元類(lèi)關(guān)鍵字:friend
友元函數(shù)
: 友元函數(shù)的特點(diǎn)是能夠訪(fǎng)問(wèn)類(lèi)中的私有成員的非成員函數(shù)。友元函數(shù)從語(yǔ)法上看,他和普通函數(shù)相同,即在定義上和調(diào)用上和普通函數(shù)相同。友元類(lèi)
: 友元類(lèi)的所有成員函數(shù)都是另一個(gè)類(lèi)的友元函數(shù),都可以訪(fǎng)問(wèn)另一個(gè)類(lèi)中的隱藏信息(包括私有成員和保護(hù)成員)。 當(dāng)希望一個(gè)類(lèi)可以存取另一個(gè)類(lèi)的私有成員時(shí),可以將該類(lèi)聲明為另一類(lèi)的友元類(lèi)。定義友元類(lèi)的語(yǔ)句格式如下: friend class 類(lèi)名; 其中:friend和class是關(guān)鍵字,類(lèi)名必須是程序中的一個(gè)已定義過(guò)的類(lèi)。 例如,以下語(yǔ)句說(shuō)明類(lèi)B是類(lèi)A的友元類(lèi): class A { … public: friend class B; … }; 經(jīng)過(guò)以上說(shuō)明后,類(lèi)B的所有成員函數(shù)都是類(lèi)A的友元函數(shù),能存取類(lèi)A的私有成員和保護(hù)成員。 - 使用友元類(lèi)時(shí)注意: (1) 友元關(guān)系不能被繼承。 (2) 友元關(guān)系是單向的,不具有交換性。若類(lèi)B是類(lèi)A的友元,類(lèi)A不一定是類(lèi)B的友元,要看在類(lèi)中是否有相應(yīng)的聲明。 (3) 友元關(guān)系不具有傳遞性。若類(lèi)B是類(lèi)A的友元,類(lèi)C是B的友元,類(lèi)C不一定是類(lèi)A的友元,同樣要看類(lèi)中是否有相應(yīng)的申明
注意事項(xiàng): 1.友元可以訪(fǎng)問(wèn)類(lèi)的私有成員。 2.只能出現(xiàn)在類(lèi)定義內(nèi)部,友元聲明可以在類(lèi)中的任何地方,一般放在類(lèi)定義的開(kāi)始或結(jié)尾。 3.友元可以是普通的非成員函數(shù),或前面定義的其他類(lèi)的成員函數(shù),或整個(gè)類(lèi)。 4.類(lèi)必須將重載函數(shù)集中每一個(gè)希望設(shè)為友元的函數(shù)都聲明為友元。 5.友元關(guān)系不能繼承,基類(lèi)的友元對(duì)派生類(lèi)的成員沒(méi)有特殊的訪(fǎng)問(wèn)權(quán)限。如果基類(lèi)被授予友元關(guān)系,則只有基類(lèi)具有特殊的訪(fǎng)問(wèn)權(quán)限。該基類(lèi)的派生類(lèi)不能訪(fǎng)問(wèn)授予友元關(guān)系的類(lèi)。
14. 繼承
繼承
:類(lèi)與類(lèi)之間的關(guān)系 父類(lèi)(基類(lèi)) 子類(lèi)(派生類(lèi)) 繼承語(yǔ)法 構(gòu)建子類(lèi)對(duì)象,先調(diào)用父類(lèi)的構(gòu)造函數(shù),再調(diào)用子類(lèi)自己的構(gòu)造函數(shù),析構(gòu)的時(shí)候先調(diào)用子類(lèi)自己的析構(gòu)函數(shù),再調(diào)用父類(lèi)的析構(gòu)函數(shù) 父類(lèi)中的public和protected的成員變量和成員函數(shù)都會(huì)被子類(lèi)繼承下來(lái)
覆蓋
: 如果子類(lèi)中有和父類(lèi)函數(shù)名相同且參數(shù)相同的成員函數(shù),則在子類(lèi)對(duì)象調(diào)用該成員函數(shù)時(shí)會(huì)把父類(lèi)的覆蓋掉
隱藏
: 如果子類(lèi)中有和父類(lèi)函數(shù)名相同但參數(shù)不同的成員函數(shù), 則會(huì)在父類(lèi)中該名稱(chēng)的成員函數(shù)會(huì)被隱藏掉
父類(lèi)的指針綁定子類(lèi)的對(duì)象 OK 子類(lèi)的指針綁定父類(lèi)的對(duì)象 error!
通過(guò)對(duì)象指針進(jìn)行的普通成員函數(shù)調(diào)用,僅僅與指針的類(lèi)型有關(guān),而與此刻指針正指向什么對(duì)象無(wú)關(guān)。想要實(shí)現(xiàn)當(dāng)指針指向不同對(duì)象時(shí)執(zhí)行不同的操作就必須將基類(lèi)中相應(yīng)的成員函數(shù)定義為虛函數(shù)。
inherit.cpp
#include <stdio.h>
#include <iostream>
using namespace std;
// 派生類(lèi)是基類(lèi)的具體化,而基類(lèi)是派生類(lèi)的抽象。
// 在多繼承時(shí),如果省略繼承方式,默認(rèn)為private
// 如果在派生類(lèi)中聲明了一個(gè)與基類(lèi)成員相同名字的函數(shù),派生類(lèi)的新成員會(huì)覆蓋基類(lèi)的同名成員
/* 不管何種繼承 基類(lèi)的私有程序都不能被派生類(lèi)繼承 否則會(huì)破壞C++的封裝特性
* 基類(lèi)的友元函數(shù)也不能被繼承,友元只是能訪(fǎng)問(wèn)指定類(lèi)的私有和保護(hù)成員的自定義函數(shù),不是被指定類(lèi)的成員,自然不能繼承
* 基類(lèi)與派生類(lèi)的靜態(tài)成員函數(shù)與靜態(tài)成員是共用一段空間的,即靜態(tài)成員和靜態(tài)成員函數(shù)是可以繼承的
*/
// public公有繼承時(shí) 基類(lèi)的公用成員public和保護(hù)成員protected在派生類(lèi)中保持原有的訪(fǎng)問(wèn)屬性,其私有成員仍為基類(lèi)私有,即在派生類(lèi)中不能訪(fǎng)問(wèn),在類(lèi)外也不能訪(fǎng)問(wèn)
// protected保護(hù)繼承 特點(diǎn)是基類(lèi)的所有公有成員和保護(hù)成員都成為派生類(lèi)的保護(hù)成員,并且只能被它的派生類(lèi)成員函數(shù)或友元訪(fǎng)問(wèn),基類(lèi)的私有成員仍然是私有的
// private私有繼承 私有繼承即所有基類(lèi)成員均變成派生類(lèi)的私有成員,基類(lèi)的私有成員仍然不能在派生類(lèi)中訪(fǎng)問(wèn)
class BASE
{
public:
void who()
{
cout << "this is base !" << endl;
}
void Fun()
{
cout << "this is base Fun ! " << endl;
}
};
class CD1:public BASE
{
public:
void who()
{
cout << "this is CD1 !" << endl;
}
};
class CD2:public BASE
{
public:
void who()
{
cout << "this is CD2 !" << endl;
}
};
int main(int argc, char* argv[])
{
CD1 obj1;
CD2 obj2;
obj1.Fun();
obj2.Fun();
obj1.who(); //this is CD1 !
obj2.who(); //this is CD2 !
return 0;
}
15. 虛函數(shù)
- 什么是虛函數(shù) 簡(jiǎn)單地說(shuō),就是在成員函數(shù)前加關(guān)鍵字virtual,這樣這個(gè)成員函數(shù)就變成了虛函數(shù)。 虛函數(shù)允許派生類(lèi)取代基類(lèi)所提供的實(shí)現(xiàn)。編譯器確保當(dāng)對(duì)象為派生類(lèi)時(shí),派生類(lèi)的實(shí)現(xiàn)總是被調(diào)用,即使對(duì)象是使用基類(lèi)指針訪(fǎng)問(wèn)而不是派生類(lèi)的指針。
在函數(shù)形參表后面寫(xiě)上= 0以指定純虛函數(shù),含有純虛函數(shù)的對(duì)象(抽象類(lèi))不能被實(shí)例化,只能作為基類(lèi)被繼承。
virtual.cpp
#include <stdio.h>
#include <iostream>
using namespace std;
/**************************
*加virtual與不加virtual的區(qū)別
***************************/
class BASE
{
public:
void who()
{
cout << "this is base !" << endl;
}
/*
virtual void who() //virtual
{
cout << "this is base !" << endl;
}
*/
};
class CD1:public BASE
{
public:
void who()
{
cout << "this is CD1 !" << endl;
}
};
class CD2:public BASE
{
public:
void who()
{
cout << "this is CD2 !" << endl;
}
};
int main(int argc, char* argv[])
{
BASE obj;
BASE *p;
CD1 obj1;
CD2 obj2;
p = &obj;
p->who();
p = &obj1;
p->who();
p = &obj2;
p->who();
obj1.who();
obj2.who();
return 0;
}
16. 模板
C++中的一個(gè)概念:泛型編程
:所謂泛型編程就是獨(dú)立于任何特定類(lèi)型的方式編寫(xiě)代碼。模板是泛型編程的基礎(chǔ)。
- 模板函數(shù): 函數(shù)模板是生成函數(shù)代碼的樣板,當(dāng)參數(shù)類(lèi)型確定后,編譯時(shí)用函數(shù)模板生成一個(gè)具有確定類(lèi)型的函數(shù),這個(gè)由函數(shù)模板而生成的函數(shù)稱(chēng)為模板函數(shù)。 模板定義以關(guān)鍵字template開(kāi)始,后接模板形參表,模板形參表是用尖括號(hào)括住的一個(gè)或多個(gè)模板形參的列表,形參之間以逗號(hào)分開(kāi)。
- inline函數(shù)模板: 函數(shù)模板可以用與非模板函數(shù)一樣的方式聲明為inline。說(shuō)明符放在模板形參表之后、返回類(lèi)型之前,不能放在關(guān)鍵字template之前。
template <typename T> inline T min(const T&, const T&); //ok
inline template <typename T> T min(const T&, const T&); //error
- 類(lèi)模板 由于篇幅有限,關(guān)注(Linux兵工廠(chǎng)),后臺(tái)回復(fù)
C++
獲取全部實(shí)例代碼。
17. 類(lèi)的大小與成員變量的訪(fǎng)問(wèn)
- 類(lèi)所占字節(jié)大小似于結(jié)構(gòu)體,與成員變量有關(guān)
#include <iostream>
using namespace std;
class A{
public:
A():m_var1(10),m_var2('a'),m_var3('b'){}
~A(){}
void disp(){
cout <<"m_var1="<<m_var1<<" m_var2="<<m_var2<<" m_var3="<<m_var3<<endl;
}
private:
int m_var1;
char m_var2;
char m_var3;
};
int main(void)
{
A a;
a.disp();
cout <<"m_var1: "<<*((int*)(&a))<< endl;
cout <<"m_var2: "<<*((char*)(&a)+4)<<endl;
cout <<"m_var3: "<<*((char*)(&a)+5)<<endl;
cout <<sizeof(a) << endl;
/*
m_var1=10 m_var2=a m_var3=b
m_var1: 10
m_var2: a
m_var3: b
8
*/
return 0;
}