2.1 class
类与结构体有所不同,类的所有方法与属性默认是私有的,而结构体中默认所有方法与属性都可以公共访问(在C语言中,结构体不能包含函数。在C++中,对结构体进行了扩展,C++的结构体可以包含函数,这样,C++的结构体也具有类的功能,与class不同的是,结构体包含的函数默认为public,而不是private。)
class Foo {
int attribute;
int function( void ) { };
};
struct Bar {
int attribute;
int function( void ) { };
};
Foo foo;
foo.attribute = 1; // WRONG
Bar bar;
bar.attribute = 1; // OK
2.2 构造函数
构造函数是一个名字与class名称相同的函数,可以为类指定零个、一个或多个构造函数。构造函数在class实例化时被调用,并且没有返回类型❗❗❗❗❗❗(不是void类型),可以设置多个构造函数,利用函数重载。
class Foo{
public:
Foo(){
std::cout<<"Foo1 called"<<std::endl;
}
Foo(int value){
std::cout<<"Foo2 called"<<std::endl;
}
}
2.3 析构函数
特点:
- 形式上:~类名(){...}
- 没有返回类型
- 每个class只能有一个
#include <iostream>
class Foo {
public:
~Foo( void )
{ std::cout << "Foo destructor called" << std::endl; }
}
int main( int argc, char **argv )
{
Foo foo();
return 0;
}
2.4 访问权限
- private:私有成员变量或函数不能直接访问,甚至不能从类外部查看。只有在类中和friend函数可以访问私有成员,类以外通过get、set函数访问,修改。C++里默认对象成员是private属性。
#include <iostream>
using namespace std;
class Box {
public:
double length;
void setWidth( double wid );
double getWidth( void );
private:
double width;
};
// Member functions definitions
double Box::getWidth(void) {
return width ;
}
void Box::setWidth( double wid ) {
width = wid;
}
// Main function for the program
int main() {
Box box;
// set box length without member function
box.length = 10.0; // OK: because length is public
cout << "Length of box : " << box.length <<endl;
// set box width without member function
// box.width = 10.0; // Error: because width is private
box.setWidth(10.0); // Use member function to set it.
cout << "Width of box : " << box.getWidth() <<endl;
return 0;
}
- public:访问没有限制,类以外任何地方都可以访问
#include <iostream>
using namespace std;
class Line {
public:
double length;
void setLength( double len );
double getLength( void );
};
// Member functions definitions
double Line::getLength(void) {
return length ;
}
void Line::setLength( double len) {
length = len;
}
// Main function for the program
int main() {
Line line;
// set line length
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
// set line length without member function
line.length = 10.0; // OK: because length is public
cout << "Length of line : " << line.length <<endl;
return 0;
}
- protected:受保护的成员变量或函数与私有成员非常相似,但是它可以在称为派生类的子类中访问它们。 protected成员可以在以下情况被访问:
- 声明该成员的class内部
- 声明该成员的class的派生类内部
- 声明该成员的class的友元函数或友元类内部
例子:
// keyword_protected.cpp
#include <iostream>
using namespace std;
class X {
public:
void setProtMemb( int i ) { m_protMemb = i; }
void Display() { cout << m_protMemb << endl; }
protected:
int m_protMemb;
void Protfunc() { cout << "\nAccess allowed\n"; }
} x;
class Y : public X {
public:
void useProtfunc() { Protfunc(); }
} y;
int main() {
// x.m_protMemb; error, m_protMemb is protected
x.setProtMemb( 0 ); // OK, uses public access function
x.Display();
y.setProtMemb( 5 ); // OK, uses public access function
y.Display();
// x.Protfunc(); error, Protfunc() is protected
y.useProtfunc(); // OK, uses public access function
// in derived class
}
2.5 初始化列表
用途:用于初始化类的数据成员,当非静态const类型数据成员需要初始化时必须使用初始化列表 www.geeksforgeeks.org/when-do-we-…
语法:: member1(参数), member2(参数)冒号开始,逗号分隔
class CMyClass {
CMyClass(int x, int y);
int m_x;
int m_y;
};
CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y)
{
};
2.6 继承
什么是继承
提供一种从现有类创建新类的方法。
基类与派生类(又称父类与子类)
基类被派生类所继承
class Student{ //base class基类
//body
};
class UnderGrad : public Student{ //derived class 派生类
//body
};
子类的对象具有:
-
子类中定义的所有成员。
-
在父类中声明的所有成员。
子类的对象可以使用(公有继承的情况):
-
子类中定义的所有公共成员。
-
父类中定义的所有公共成员。
继承的语法
声明一个派生类:
class derived-class: access-specifier base-class
如果未使用访问说明符(access-specifier 即public private protected),则默认情况下为私有继承。
多重继承
一个c++类可以从多个类继承成员
多重继承语法实例:
#include <iostream>
using namespace std;
// Base class Shape
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost {
public:
int getCost(int area) {
return area * 70;
}
};
// Derived class多重继承
class Rectangle: public Shape, public PaintCost {
public:
int getArea() {
return (width * height);
}
};
int main(void) {
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
公共继承 私有继承 保护继承
公共继承:
从公共基类派生一个类时,基类的公共成员成为派生类的公共成员,基类的受保护成员成为派生类的受保护成员。 不能直接从派生类访问基类的私有成员,但可以通过调用基类的公共成员和受保护成员来访问它们。
公有继承的派生类的对象也是其基类对象。
受保护的继承: 从受保护的基类派生时,基类的公共成员和受保护成员成为派生类的受保护成员。
私有继承: 从私有基类派生时,基类的公共成员和受保护成员成为派生类的私有成员,❗❗注意私有继承的派生类的对象仅仅是继承了基类的成员,其对象不是基类的对象。 可以视为一种组合(composition),组合大于继承的组合。
一篇深入解析私有继承的文章:www.bogotobogo.com/cplusplus/p…
2.7 多态
多态Polymorphism, 在C ++中,多态意味着如果我们调用成员函数,则可能导致执行不同的函数,具体取决于调用它的对象的类型。
虚函数
虚函数是成员函数,它在基类中使用关键字virtual声明,并由派生类重新定义(overriden)。通过它我们可以实现多态。
案例1 不使用虚函数
#include <iostream>
using namespace std;
// Base class
class Shape {
public:
Shape(int l, int w){length = l; width=w;} //default constructor
int get_Area(){cout << "This is call to parent class area"<<endl;}
protected:
int length,width;
};
// Derived class
class Square: public Shape {
public:
Square(int l=0, int w=0) : Shape(l,w) {} //declaring and initializing derived class constructor
int get_Area(){
cout << "Square area: " << length*width << endl;
return (length * width);
}
};
// Derived class
class Rectangle: public Shape {
public:
Rectangle(int l=0, int w=0) : Shape(l,w) {} //declaring and initializing derived class constructor
int get_Area(){ cout << "Rectangle area: " << length*width << endl;return (length * width); }
};
int main(void) {
Shape *s;
Square sq(5,5); //making object of child class Sqaure
Rectangle rec(4,5); //making object of child class Rectangle
s = &sq;
s->get_Area();
s= &rec;
s->get_Area();
return 0;
}
该案例代码中,Shape作为基类,Square,Rectangle作为派生类,基类,派生类均存在名为int get_Area()的成员函数,我们期望s指针能够分别调用对应的派生类中的get_Area(),实际结果却是调用了基类的get_Area()。
这是由于静态链接导致的,编译器仅设置了一次对get_Area()的调用(调用基类中的get_Area())。
使用虚函数
如果在基类中定义了虚函数,而在派生类中定义了另一个版本,则将向编译器发出信号,表明我们不需要该函数的静态链接。
#include <iostream>
using namespace std;
// Base class
class Shape {
public:
Shape(int l, int w){length = l; width=w;} //default constructor
//changing get_Area() to virtual type function
virtual int get_Area(){cout << "This is call to parent class area"<<endl;}
protected:
int length,width;
};
// Derived class
class Square: public Shape {
public:
Square(int l=0, int w=0) : Shape(l,w) {} //declaring and initializing derived class constructor
int get_Area(){
cout << "Square area: " << length*width << endl;
return (length * width);
}
};
// Dreived class
class Rectangle: public Shape {
public:
Rectangle(int l=0, int w=0) : Shape(l,w) {} //declaring and initializing derived class constructor
int get_Area(){ cout << "Rectangle area: " << length*width << endl;return (length * width); }
};
int main(void) {
Shape *s;
Square sq(5,5); //making object of child class Sqaure
Rectangle rec(4,5); //making object of child class Rectangle
s = &sq;
s->get_Area();
s= &rec;
s->get_Area();
return 0;
}
输出:
可见使用虚函数可以实现依据对象调用函数。
2.8 抽象
数据抽象是指只向外部提供必要的信息,而隐藏它们的背景细节,在C ++中,类提供了较高级别的数据抽象。 它们向外界提供了足够的公共方法来操作对象的数据(即状态)而实际上并不知道内部如何实现类。例如,您的程序可以调用sort()函数,而不知道该函数实际使用什么算法对给定的值进行排序。实际上,排序功能的底层实现可能会在库的不同版本之间发生变化,只要接口保持不变,函数调用就仍然可以工作。
#include <iostream>
using namespace std;
class Adder {
public:
// constructor
Adder(int i = 0) {
total = i;
}
// interface to outside world
void addNum(int number) {
total += number;
}
// interface to outside world
int getTotal() {
return total;
};
private:
// hidden data from outside world
int total;
};
int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
Adder类将数字相加,然后返回总和。 公共成员addNum和getTotal是与外界的接口,用户需要了解它们才能使用该类。 私有成员total是用户不需要了解的,但对于类的正常运行是必需的。
抽象将代码分离为接口和实现。因此,在设计组件时,必须保持接口独立于实现,这样,如果更改了底层实现,则接口将保持不变。
2.9 封装
封装是一种面向对象的编程概念,它将数据和操作数据的函数绑定在一起:
class Box {
public:
double getVolume(void) {
return length * breadth * height;
}
private:
double length ; // Length of a box
double breadth; // Breadth of a box
double height ; // Height of a box
};
length breadth height 数据与getVolume方法绑定在一起,封装到Box类。
数据封装是一种绑定数据与使用它们的函数的机制,而数据抽象是一种只公开接口并对用户隐藏实现细节的机制
3.0 抽象类(C++的接口)
接口描述了C ++类的行为或功能,而无需承诺该类的特定实现。
C ++接口是使用抽象类实现的,这些抽象类不应与数据抽象混淆,数据抽象是将实现细节与关联数据分开的概念。
通过将至少一个函数声明为纯虚函数,可以使一个类抽象。 通过在声明中放置“ = 0”来指定纯虚函数,如下所示:
class Box {
public:
// pure virtual function 纯虚函数
virtual double getVolume() = 0;
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
抽象类 例子
#include <iostream>
using namespace std;
// Base class
class Shape {
public:
// pure virtual function providing interface framework.
virtual int getArea() = 0;
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Derived classes
class Rectangle: public Shape {
public:
int getArea() {
return (width * height);
}
};
class Triangle: public Shape {
public:
int getArea() {
return (width * height)/2;
}
};
int main(void) {
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// Print the area of the object.
cout << "Total Triangle area: " << Tri.getArea() << endl;
return 0;
}
当编译并执行上述代码时,会产生以下结果
Total Rectangle area: 35
Total Triangle area: 17
可以看到抽象类是如何根据getArea()定义接口的,另外两个类实现了相同的函数,但使用不同的算法来计算特定于形状的区域。
3.1 友元
友元函数(friend function)
类的friend函数定义在类的作用域之外,但它有权访问类的所有私有和受保护的成员。尽管friend函数的原型出现在类定义中,但friend不是成员函数。
#include <iostream>
using namespace std;
class Box {
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// Member function definition
void Box::setWidth( double wid ) {
width = wid;
}
// Note: printWidth() is not a member function of any class.
void printWidth( Box box ) {
/* Because printWidth() is a friend of Box, it can
directly access any member of this class */
cout << "Width of box : " << box.width <<endl;
}
// Main function for the program
int main() {
Box box;
// set box width without member function
box.setWidth(10.0);
// Use friend function to print the wdith.
printWidth( box );
return 0;
}
友元类
Friend类可以访问其他类的私有和受保护的成员,例如在下面的代码中,一个LinkedList类可以被允许访问Node类的私有成员
_none
edit
play_arrow
brightness_4
class Node {
private:
int key;
Node* next;
/* Other members of Node Class */
friend int LinkedList::search();
// Only search() of linkedList
// can access internal members
};
和Friend类一样,Friend函数可以被授予特殊权限来访问私有和受保护的成员。一个友元函数可以是:
- 另一个类的方法
- 一个全局函数
class Node {
private:
int key;
Node* next;
/* Other members of Node Class */
friend int LinkedList::search();
// Only search() of linkedList
// can access internal members
};
注意点:
-
友元只能用于有限的目的。 太多的函数或外部类被声明为具有受保护或私有数据的类的朋友,这降低了在面向对象编程中封装单独类的价值。
-
友元不是相互的。 如果A类是B的朋友,那么B不会自动成为A的朋友。
-
友元不是继承的