C++基础03
this 指针
1
| 一个类的不同对象在调用自己的成员函数时,其实他们调用的是同一段函数代码,成员函数通过this指针能够得知要访问的对象的数据成员
|
1
| 每个对象都拥有一个this指针,this指针记录对象的内存地址
|
1
| 指向类自身数据的指针,简单来说就是指向当前类的当前实例对象
|
类的this指针特点
- 只能在成员函数中使用
- this指针在成员函数开始前构造,在成员函数结束后清除
- 会因为不同的编译器存放在不同的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| linux@ubuntu:~/11_3$ cat test01.cpp #include<iostream> using namespace std;
class Dog { public: string name; void func();
}; int main() { Dog dog; dog.func(); return 0; } void Dog::func() { this->name="雪碧"; cout<< "狗狗的名字是:"<<this->name<<endl;
} linux@ubuntu:~/11_3$ ./test01 狗狗的名字是:雪碧
|
继承
1
| 当创建一个类时,不需要重新编写新的数据成员和数据函数,只需要指定新建的类继承一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
|
一个类可以派生自多个类,它可以从多个基类继承函数和数据。定义一个派生类,使用一个类派生列表来指定基类,类派生列表以一个或多个基类命名,如:
1
| class derived-class:access-specifier base-class
|
与类的访问修饰限定符一样,继承的方式有几种。其中,访问修饰符access-specifier是public、protected或private中的一个,base-class是之前定义的某个类的名称。如果未使用访问修饰符,则默认为private
public(公有继承):当一个类派生继承公有基类时,基类的公有成员也是派生类的公有成员。基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
protected(保护继承):当一个类派生继承保护基类时,基类的公有和保护成员将成为派生类的保护成员
private(私有继承):当一个派生继承私有基类时,基类的公有和保护成员将成为派生类的私有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| linux@ubuntu:~/11_3$ vim test02.cpp linux@ubuntu:~/11_3$ g++ test02.cpp -o test02 linux@ubuntu:~/11_3$ ./test02 狗狗的名字是:雪碧 狗狗的年龄是:18 狗狗的颜色是:绿色 狗狗的体重是:45 linux@ubuntu:~/11_3$ cat test02.cpp #include<iostream> #include<string> using namespace std;
class Animal { public: string color; int weight;
};
class Dog:public Animal { public: string name; int age; void run(); };
int main() { Dog dog; dog.name="雪碧"; dog.age=18; dog.color="绿色"; dog.weight=45; cout<< "狗狗的名字是:"<<dog.name<<endl; cout<< "狗狗的年龄是:"<<dog.age<<endl; cout<< "狗狗的颜色是:"<<dog.color<<endl; cout<< "狗狗的体重是:"<<dog.weight<<endl;
return 0; }
|
重载
函数重载和运算符重载
重载声明是指一个与之前已经在该作用域声明过的函数或方法具有相同名称的声明,当它们的参数列表和定义不同。
选择合适的重载函数或重载运算符的过程,称为重载决策
函数重载
在同一作用域,可以声明几个功能类似的同名函数,但是这些同名函数形式参数(参数的个数、类型或顺序)必然不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| linux@ubuntu:~/11_3$ vim test03.cpp linux@ubuntu:~/11_3$ g++ test03.cpp -o test03 linux@ubuntu:~/11_3$ ./test03 雪碧的体重是:20KG 雪碧的体重是:22.5KG linux@ubuntu:~/11_3$ cat test03.cpp #include<iostream> #include<string> using namespace std;
class Dog { public: string name; void getWeight(int weight){ cout<<name<<"的体重是:"<<weight<<"KG"<<endl;
} void getWeight(double weight){ cout<<name<<"的体重是:"<<weight<<"KG"<<endl; } }; int main() { Dog dog; dog.name="雪碧"; dog.getWeight(20); dog.getWeight(22.5); return 0; }
|
运算符重载
实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够用同名的函数来完成不同的基本操作。
要重载运算符时,需要使用被称为运算符函数的特殊函数形式:operatorp(argument-list),operator后面的p为要重载的运算符符号,重载运算符格式:
1 2 3 4
| <返回类型说明符>operator<运算符符号>(<参数列表>) { <函数> }
|
可重载的运算符列表:
双目算术运算符 |
|
关系运算符 |
|
逻辑运算符 |
|
单目运算符 |
+,-,*(指针),&(取地址) |
自增自减运算符 |
|
位运算符 |
|
赋值运算符 |
|
空间申请与释放 |
new,delete,new[],delete[] |
其他运算符 |
()(函数调用),->(成员访问),,(逗号运算符),[](下标) |
不可重载的运算符列表
成员访问运算符 |
. |
成员指针访问运算符 |
.*,-> |
域运算符 |
:: |
长度运算符 |
sizeof() |
条件运算符 |
?: |
预处理符号 |
# |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| linux@ubuntu:~/11_3$ ./test04 第三只小狗的体重为:30KG linux@ubuntu:~/11_3$ cat test04.cpp #include<iostream> #include<string> using namespace std;
class Dog { public: int weight; Dog operator+(const Dog &d){ Dog dog; dog.weight=this->weight+d.weight; return dog; }
}; int main() { Dog dog1; Dog dog2; Dog dog3; dog1.weight=10; dog2.weight=20; dog3=dog1+dog2; cout<<"第三只小狗的体重为:"<<dog3.weight<<"KG"<<endl; return 0; }
|
多态
1
| c++多态意味着调用成员函数是,会根据调用函数的对象的类型来执行不同的函数
|
1 2 3 4 5 6 7 8 9 10 11
| 形成多态必须具备的三个条件: 1、必须在继承关系 2、继承关系必须有同名虚函数 3、存在某类类型的指针或者引用,通过指针或引用调出虚函数 虚函数: 是在某类中使用关键字virtual声明的函数,在派生类中重新定义其类中定义的虚函数时,会告诉编译器不要静态链接到该函数 virtual ReturnType FunctionName(Parameter) 纯虚函数: 若在基类中定义虚函数,以便在派生类中重新定义该函数更好的适用于对象,但是在基类中有不能对虚函数给出有意义的实现,这个时候会用到纯虚函数。声明如下: virtual void function1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但是可以
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| linux@ubuntu:~/11_3$ ./test05 Dog的run()方法 Cat的run()方法 linux@ubuntu:~/11_3$ cat test05.cpp #include<iostream> #include<string> using namespace std;
class Animal { public: virtual void run(){ cout<<"Animal的run()方法"<<endl;
} }; class Dog:public Animal { public: void run() { cout<<"Dog的run()方法"<<endl;
} };
class Cat:public Animal { public: void run(){
cout<<"Cat的run()方法"<<endl; } }; int main() { Animal *animal; //声明一个Animal的指针对象,(没有实例化) Dog dog; //实例化dog对象 Cat cat; //实例化cat对象 animal=&dog; //存储dog对象的地址 animal->run(); //调用run()方法 animal=&cat; //存储cat对象的地址 animal->run(); //调用run()方法 return 0; }
|
数据封装
封装是面向对象编程中把数据和操作的函数绑定在引起的的一个概念,这样能避免受到外界的干扰和误用,确保安全。数据封装申引出另一个重要的OPP概念,即数据隐藏。
数据封装是一种把数据和操作数据的函数绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制,C++通过创建类来支持封装和数据隐藏(public,protected,private)。
数据抽象是指只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不是呈现细节。数据抽象是一种依赖于接口和实现分离的编程技术。
数据抽象的好处:
- 类的内部受到保护,不会因无意的用户级错误导致对象状态受损
- 类实现可能随时间的推移而变化,以便应对不断变化的需求,或者应对那些要求不改变用户级的错误信息。
在C++程序中,任何带有公有和私有成员的类都在C++程序中,任何带有公有和私有成员的类都可以作为数据封装和数据抽象的实例。通常情况下,会设置成员状态为私有(private),除非我们需要将其暴露,这样才能保持良好的封装性。这通常应用于数据成员,但它同样适用于所有的成员,包括虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| linux@ubuntu:~/11_3$ vim test06.cpp linux@ubuntu:~/11_3$ g++ test06.cpp -o test06 linux@ubuntu:~/11_3$ ./test06 雪碧一共获得了9份食物 linux@ubuntu:~/11_3$ cat test06.cpp #include<iostream> #include<string> using namespace std;
class Dog { public: string name; Dog(int i=0){ total=i;
} void addFood(int number){ total=total+number;
} int getFood(){ return total; } private: int total;
}; int main() { Dog dog; dog.name="雪碧"; dog.addFood(6); dog.addFood(3); cout<<dog.name<<"一共获得了"<<dog.getFood()<<"份食物"<<endl; return 0; }
|
接口(抽象类)
接口描述了类的行为和功能,而不需要完成类的特定实现。C++接口是使用抽象类来实现的,抽象类与数据抽象互补混淆,数据抽象是一个把实现细节于相关的数据分离开的概念。如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用”=0”来指定的
设计抽象类(ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能用于实例化对象,只能作为接口使用。如果试图实例化一个抽象类的对象,会导致编译错误。
因此,如果一个ABC的子类需要被实例化,则必须实现每一个虚函数,这也意味着C++支持使用ABC声明接口。如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。可用于实例化对象的类被称为具体类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| linux@ubuntu:~/11_3$ vim test07.cpp linux@ubuntu:~/11_3$ g++ test07.cpp -o test07 linux@ubuntu:~/11_3$ ./test07 Dog的run()方法 Cat的run()方法 linux@ubuntu:~/11_3$ cat test07.cpp #include<iostream> #include<string> using namespace std;
class Animal { public: virtual void run()=0; }; class Dog:public Animal { public: void run(){ cout<<"Dog的run()方法"<<endl; } }; class Cat:public Animal { public: void run(){ cout<<"Cat的run()方法"<<endl; } }; int main(){
Dog dog; Cat cat; dog.run(); cat.run(); return 0; }
|