对象:
抽象与分类
例如:抽象实例——钟表
// 数据抽象:
int hour,int minute,int second
//代码抽象:
setTime(),showTime()
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;};
封装
例:
class Clock {
public: void setTime(int newH, int newM, int newS);
void showTime();
private: int hour, minute, second;
};
继承
多态
避免命名冲突
std 是 C++标准库的命名空间( namespace)名
using namespace std 表示打开 std 命名空间
必须要加括号
,防止宏定义中部分替换而导致的运行错误。指向常量的指针:
不能改变常量的值,但是可以改变指针的地址const char * name = "change" ;
// 可以理解为,const *name ,其中*name 是一个普通常量,因此常量的值是不能改变的
因此:name[3] = 'a' 是不可法的
name = "zhang" 是合法的,相当于 string t = "zhang" , name = &t;//这个时候仅仅只是改变了name指针变量所指向的地址。
常指针:
不能改变指针的地址,但是可以改变指针所指向常量的值
char * const name = "chen";
name[3] = 'a'; //合法
name = "zhang" //不合法
指向常量的长指针:
不能改变指针的地址,也不能改变指针所指向常量的值
tip: name 指针变量,表示地址。不好理解的话,可以想到数组,数组就是一个特殊的指针,int a[10] ,其中a,就是表示数组a[0]的地址。
如果还是不能理解的话可以看看c语言中指针的知识点:
这是本人写的相关博客连接:c语言重难点笔记
函数原型格式:返回类型名 函数名 (参数表);
//注意这个分号
c 与 c++ 的区别在于:
引用(&)是标识符的别名;
int i, j;
int &ri = i; //定义 int 引用 ri,并初始化为变量 i 的引用
j = 10;
ri = j; //相当于 i = j;
指针与引用的差别:
#include
using namespace std;
void swap1(int& a, int& b) {
int t = a;
a = b;
b = t;
}
void swap2(int a, int b) {
int t = a;
a = b;
b = t;
}
int swap3(int a, int b) {
int t = a;
a = b;
b = t;
return a,b;
}
int main() {
int x = 5, y = 10;
cout<<"x = "<<x<<" y = "<<y<<endl;
swap1(x, y);
cout<<"x = "<<x<<" y = "<<y<< endl; //可以直接改变x,y的值
swap2(x, y); //由于没有返回值,形参的改变不会影响实参
x , y = swap3(x,y) //这样才可以改变
return 0;
motivation: 在函数进行调用时,系统会将程序当前的一些状态信息(例如现场和返回地址等)存到栈中,同时转到函数的代码处去执行函数体语句,这些参数保存与传递的过程需要时间和空间的开销,这样就会降低程序执行的效率。为了消除函数调用时的系统开销,以提高运行效率。
简单的说:编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销,用空间换取时间的策略。
#include
using namespace std;
const double PI = 3.14159265358979;
inline double calArea(double radius) {
return PI * radius * radius;
}
int main() {
double r = 3.0;
double area = calArea(r);
cout << area << endl;
return 0;
}
int add(int x = 5,int y = 6) {
return x + y;
}
int main() {
add(10,20); //10+20
add(10); //10+6
add(); //5+6
}
int add(int x, int y = 5, int z = 6);//正确
int add(int x = 1, int y = 5, int z);//错误
int add(int x = 1, int y, int z = 6);//错误
//原型声明在前
int add(int x = 5,int y = 6);
int main() {
add();
}
int add(int x,int y) {
//此处不能再指定默认值
return x + y;
}
//只有定义,没有原型声明
int add(int x = 5,int y = 6) {
return x + y;
}
int main() {
add();
}
motivation: 解决c语言中,函数名必须唯一的不便之处。
C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。
int add (int x ,int y);
float add(float x , float y);
int add (int x , int y) ;
int add(int x , int y, int z);
注意:
int add (int x, int y); int add (int a , int b) ;
/ /错误!编译器不以形参名来区分函数
int add (int x ,inty); void add(int x , int y);
//错误!编译器不以返回值来区分函数
例题:
编写两个名为 sumOfSquare 的重载函数,分别求两整数的平方和及两实数的平方和。
#include
using namespace std;
int sumOfSquare(int a, int b) {
return a * a + b * b;
}
double sumOfSquare(double a, double b) {
return a * a + b * b;
}
int main() {
int m, n;cout << "Enter two integer: ";
cin >> m >> n;
cout<<"Their sum of square: "<<sumOfSquare(m, n)<<endl;
double x, y;
cout << "Enter two real number: ";
cin >> x >> y;
cout<<"Their sum of square: "<<sumOfSquare(x, y)<<endl;
return 0;
}
运行结果:
Enter two integer: 3 5
Their sum of square: 34
Enter two real number: 2.3 5.8
Their sum of square: 38.93
运算符new用于内存分配的最基本形式为:
指针变量名 = new 类型;
原理:从堆的一块自由存储区中为程序分配一块与类型字节数相适应的内存空间,并将该块内存的首地址存于指针变量。
int *p;
p = new int; //动态分配一个整型存储空间,并将首地址赋值
……
delete p; //释放指针p指向的存储空间
//动态创建对象举例
#include
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout<<"Default Constructor called."<<endl;
}
Point(int x, int y) : x(x), y(y) {
cout<< "Constructor called."<<endl;
}
~Point() { cout<<"Destructor called."<<endl; }
int getX() const { return x; }
int getY() const { return y; }
void move(int newX, int newY) {
x = newX;
y = newY;
}
private:
int x, y;
};
int main() {
cout << "Step one: " << endl;
Point *ptr1 = new Point; //调用默认构造函数
delete ptr1; //删除对象,自动调用析构函数
cout << "Step two: " << endl;
ptr1 = new Point(1,2);
delete ptr1;
return 0;
}
运行结果:
Step One:
Default Constructor called.
Destructor called.
Step Two:
Constructor called.
Destructor called.
动态创建对象数组举例
#include
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout<<"Default Constructor called."<<endl;
}
Point(int x, int y) : x(x), y(y) {
cout<< "Constructor called."<<endl;
}
~Point() { cout<<"Destructor called."<<endl; }
int getX() const { return x; }
int getY() const { return y; }
void move(int newX, int newY) {
x = newX;
y = newY;
}
private:
int x, y;
};
int main() {
Point *ptr = new Point[2]; //创建对象数组
ptr[0].move(5, 10); //通过指针访问数组元素的成员
ptr[1].move(15, 20); //通过指针访问数组元素的成员
cout << "Deleting..." << endl;
delete[] ptr; //删除整个对象数组
return 0;
}
运行结果:
Default Constructor called.
Default Constructor called.
Deleting...
Destructor called.
Destructor called.
int *pArr = (int *)malloc(sizeof(int)*len);
//表示我们的操作系统,要为我们的程序分配len个存储空间,可以进行读写
// malloc函数只返回第一个字节的地址,这样无法区分数组的类型,干地址
//因此需要用一个强制类型转换
相同点:
都是用于内存的动态分配
差异:
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
}
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour = 0, minute = 0, second = 0;
}
成员函数在类外的定义:
返回值类型 类名::成员函数名(参数表){
函数体
}
“::” 作用域运算符,在类名和函数名之间加上作用域运算符“::” ,用于声明这个成员函数时属于哪几类的;在局部变量的作用域内使用使用同名的全局变量,可以使用 “::",来区分局部变量和全局变量,::变量名 表示全局变量。
这种方式有利于减少类体的长度,增加类的可读性,将类的接口与类的实现细节相分离,隐藏执行细节。
数据成员和成员函数一般统称为类的成员。
注意:
在默认情况下(即没有指定属于私有或共有时),类中的成员是私有的。因此一个类中没有申明成员类型,则成员都为私有类型成员,则类外对象不能访问类内成员
例 钟表类
类的定义
#include
using namespace std;
class Clock{
public:
void setTime(int newH = 0, int newM = 0, int newS = 0);
void showTime();private:
int hour, minute, second;
}
成员函数的实现
void Clock::setTime(int newH, int newM, int newS) {
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime() {
cout << hour << ":" << minute << ":" << second;
}
对象的使用
int main() {
Clock myClock;
myClock.setTime(8, 30, 30);
myClock.showTime();
return 0;
}
函数名与类名相同;
不能定义返回值类型,也不能有return语句;
可以有形式参数,也可以没有形式参数;
可以是内联函数;
可以重载;
可以带默认参数值。
//构造函数例题:
class Clock {
public:
Clock(int newH,int newM,int newS);//构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
//构造函数的实现:
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM),
second(newS) {
}
int main() {
Clock c(0,0,0); //自动调用构造函数
c.showTime();
return 0;
}
//构造函数例题二
class Clock {
public:
Clock(int newH, int newM, int newS); //构造函数
Clock(); //默认构造函数void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(): hour(0),minute(0),second(0){ }//默认构造函数
//其它函数实现同前
int main() {
Clock c1(0, 0, 0); //调用有参数的构造函数
Clock c2; //调用无参数的构造函数
……
}
motivation
复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已
存在的对象去初始化同类型的新对象。
class 类名 {
public :
类名(形参);//构造函数
类名(const 类名 &对象名);//拷贝构造函数
// ...
};
类名::类( const 类名 &对象名)//拷贝构造函数的实现 采用引用的方式进行赋值
{ 函数体 }
例 Point 类的完整程序
class Point { //Point 类的定义
public:
Point(int xx=0, int yy=0) { x = xx; y = yy; } //构造函数,内联
Point(const Point& p); //复制构造函数
void setX(int xx) {x=xx;}
void setY(int yy) {y=yy;}
int getX() const { return x; } //常函数
int getY() const { return y; } //常函数
private:
int x, y; //私有数据
};
//拷贝构造函数的实现
Point::Point (const Point& p) {
x = p.x;
y = p.y;
cout << "Calling the copy constructor " << endl;
}
//形参为Point类对象void fun1(Point p) {
cout << p.getX() << endl;
}
//返回值为Point类对象Point fun2() {
Point a(1, 2);
return a;}
int main() {
Point a(4, 5);
Point b(a); //用a初始化b。
cout << b.getX() << endl;
fun1(b); //对象b作为fun1的实参
b = fun2(); //函数的返回值是类对象
cout << b.getX() << endl;
return 0;
}
构造函数和析构函数举例
#include
using namespace std;
class Point {
public:
Point(int xx,int yy);
~Point();
//...其他函数原型
private:
int x, y;
};
• 为什么会出现结构体:为了表示一些复杂的数据,而普通的基本类型变量无法满足要求;
• 定义:结构体是用户根据实际需要自己定义的复合数类型;
//定义结构体
#include
struct Student
{
int sid;
char name[200];
int age;
};
struct Student st = {1001,"zhangsan",18};//整体赋值,类似于Java中new类的构造函数
st.id=1001;//单个赋值
strcpy(st.name,"zhangsan");
st.age=18;
struct Student *pst = &st;//通常使用指针的方式赋值
//pst所指向的结构体变量中的sid这个成员
pst->sid=1001;//==(*pst).sid==st.sid
strcpy(pst->name,"lisi");
pst->age=19;
结构体在定义一个对象后,系统为自定进行初始化,数值类型为0,字符类型为空。
• 注意事项 结构体变量不能算术计算,但是可以赋值;
typedef struct person{
char name[6];
int yy,mm,dd;
person(){}
person(int _yy,int _mm,int _dd){
yy = _yy;
mm = _mm;
dd = _dd;
}
}PER;
具体应用可以参考本人写的结构体应用博客:结构体应用解析
对象数组的定义与访问
对象类数组初始化:
数组元素所属类的构造函数
#include
using namespace std;
class Point { //类的定义
public: //外部接口
Point();
Point(int x, int y);
~Point();
void move(int newX,int newY);
int getX() const { return x; }
int getY() const { return y; }
static void showCount(); //静态函数成员
private: //私有数据成员
int x, y;
};
Point::Point() : x(0), y(0) {
cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y) : x(x), y(y) {
cout << "Constructor called." << endl;
}
Point::~Point() {
cout << "Destructor called." << endl;
}
void Point::move(int newX,int newY) {
cout << "Moving the point to (" << newX << ", " << newY << ")" << endl;
x = newX;
y = newY;
}
int main() {
cout << "Entering main..." << endl;
Point a[2];
for(int i = 0; i < 2; i++)
a[i].move(i + 10, i + 20);
cout << "Exiting main..." << endl;
return 0;
}
//使用指针来访问 Point 类的成员
#include
using namespace std;
class Point {
public:
Point(int x = 0, int y = 0) : x(x), y(y) { }
int getX() const { return x; }
int getY() const { return y; }
private:
int x, y;
};
int main() {
Point a(4, 5);
Point *p1 = &a; //定义对象指针,用a的地址初始化
cout << p1->getX() << endl;//用指针访问对象成员
cout << a.getX() << endl; //用对象名访问对象成员
return 0;
}
#include
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0) : x(x), y(y) { //构造函数
//在构造函数中对count累加,所有对象共同维护同一个count
count++;
}
Point(Point &p) { //拷贝构造函数
x = p.x;
y = p.y;count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
void showCount() { //输出静态数据成员
cout << " Object count = " << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() { //主函数
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //输出对象个数
return 0;
}
运行结果:
Point A: 4, 5 Object count=1
Point B: 4, 5 Object count=2
#include
using namespace std;
class Point {
public:
Point(int x = 0, int y = 0) : x(x), y(y) { count++; }//构造函数
Point(Point &p) { //拷贝构造函数
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
static void showCount() {
cout << " Object count = " << count << endl;
}
private:
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() {
Point a(4, 5); //定义对象a,其构造函数会使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
Point::showCount(); //输出对象个数Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
Point::showCount(); //输出对象个数
return 0;
}
#include
#include
class Point { //Point类声明
public: //外部接口
Point(int x=0, int y=0) : x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(Point &a, Point &b);
private: //私有数据成员
int x, y;
};
float dist( Point& a, Point& b) {
double x = a.x - b.x;
double y = a.y - b.y;return static_cast<float>(sqrt(x * x + y * y));
}
int main() {
Point p1(1, 1), p2(4, 5);
cout <<"The distance is: ";
cout << dist(p1, p2) << endl;
return 0;
}
class A {
friend class B;
public:
void display() {
cout << x << endl;
}
private:
int x;
};
class B {
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i) {
a.x=i;
}
void B::display() {
a.display();
}
如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A
类的成员函数却不能访问B类的私有、保护数据。
必须进行初始化,不能被更新。 const 类名 对象名
class A
{
public:
A(int i,int j) {x=i; y=j;}
...
private:
int x,y;
};
A const a(3,4); //a是常对象,不能被更新
用const进行修饰的类成员:常数据成员和常函数成员
常成员函数
- 使用const关键字说明的函数。
- 常成员函数不更新对象的数据成员。
- 常成员函数说明格式:
类型说明符 函数名(参数表)const;
这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键
字。
- const关键字可以被用于参与对重载函数的区分
常数据成员
//常成员函数举例
#include
using namespace std;
class R {
public:
R(int r1, int r2) : r1(r1), r2(r2) { }
void print();
void print() const;
private:
int r1, r2;
};
void R::print() {
cout << r1 << ":" << r2 << endl;
}
void R::print() const {
cout << r1 << ";" << r2 << endl;
}
int main() {
R a(5,4);
a.print(); //调用void print()
const R b(20,52);
b.print(); //调用void print() const
return 0;
}
#include
using namespace std;
class A {
public:
A(int i);
void print();
private:
const int a;
static const int b; //静态常数据成员
};
const int A::b=10;
A::A(int i) : a(i) { }
void A::print() {
cout << a << ":" << b <<endl;
}
int main() {
//建立对象a和b,并以100和0作为初值,分别调用构造函数,
//通过构造函数的初始化列表给对象的常数据成员赋初值
A a1(100), a2(0);
a1.print();
a2.print();
return 0;
}
常引用
被引用的对象不能被更新,const 类型说明符 &引用名
#include
#include
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0): x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(const Point &p1,const Point &p2);
private: //私有数据成员
int x, y;
};
float dist(const Point &p1, const Point &p2) {
double x = p1.x - p2.x;double y = p1.y - p2.y;
return static_cast<float>(sqrt(x*x+y*y));
}
int main() { //主函数
const Point myp1(1, 1), myp2(4, 5);
cout << "The distance is: ";
cout << dist(myp1, myp2) << endl;
return 0;
}
string s1;
string(const char *s); //用指针s所指向的字符串常量初始化string对象
string s2 = “abc”;
string(const string& rhs); //拷贝构造函数
string s3 = s2;
string 类常用操作
string s1 = "abc", s2 = "def";
string s3 = s1 + s2; //结果是"abcdef"
bool s4 = (s1 < s2); //结果是true
char s5 = s2[1]; //结果是'e'
//string 类应用举例
#include
#include
using namespace std;
//根据value的值输出true或false
//title为提示文字
inline void test(const char *title, bool value)
{
cout << title << " returns "<< (value ? "true" : "false") << endl;
}
int main() {
string s1 = "DEF";
cout << "s1 is " << s1 << endl;
string s2;
cout << "Please enter s2: ";
cin >> s2;
cout << "length of s2: " << s2.length() << endl;
//比较运算符的测试
test("s1 <= \"ABC\"", s1 <= "ABC");
test("\"DEF\" <= s1", "DEF" <= s1);//连接运算符的测试
s2 += s1;
cout << "s2 = s2 + s1: " << s2 << endl;
cout << "length of s2: " << s2.length() << endl;
return 0;
}
注意cin的>>操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输
入时被读取,因此,对于含有空格
的string 类,则需要用getline可以输入整行字符串(要包string头文件),例如:
// getline 输入字符串
include <iostream>
#include
using namespace std;
int main() {
for (int i = 0; i < 2; i++){
string city, state;
getline(cin, city, ',');
getline(cin, state);
cout << "City:" << city << “ State:" << state << endl;
}
return 0;
}
运行结果:
Beijing,China
City: Beijing State: China
San Francisco,the United StatesCity: San Francisco State: the United States
语法
class 派生类名:继承方式 基类名
{
成员声明;
}
class Derived: public Base
{
public:
Derived ();
~Derived ();
};
语法
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,...
{
成员声明;
}
注意: 每一个“继承方式”,只用于限制对紧随其后之基类的继承。
class Derived: public Base1, private Base2
{
public:
Derived ();
~Derived ();
};
不同继承方式的影响主要体现在:
三种继承方式
保持不变
;不可直接访问
。private
成员;public
成员。#include
#include
using namespace std;
class Point { //基类Point类的定义
public: //公有函数成员
void initPoint(float x = 0, float y = 0)
{ this->x = x; this->y = y;}
void move(float offX, float offY)
{ x += offX; y += offY; }
float getX() const { return x; }
float getY() const { return y; }private: //私有数据成员
float x, y;
};
class Rectangle: public Point { //派生类定义部分
public: //新增公有函数成员
void initRectangle(float x, float y, float w, float h) {
initPoint(x, y); //调用基类公有成员函数
this->w = w;
this->h = h;
}
float getH() const { return h; }
float getW() const { return w; }
private: //新增私有数据成员
float w, h;
};
int main() {
Rectangle rect; //定义Rectangle类的对象
//设置矩形的数据
rect.initRectangle(2, 3, 20, 10);
rect.move(3,2); //移动矩形位置
cout << "The data of rect(x,y,w,h): " << endl;//输出矩形的特征参数
cout << rect.getX() <<", "<< rect.getY() << ", "<< rect.getW() << ", "<< rect.getH() <<endl;
return 0;
}
私有继承
#include
#include
using namespace std;
class Point { //基类Point类的定义
public: //公有函数成员
void initPoint(float x = 0, float y = 0)
{ this->x = x; this->y = y;}
void move(float offX, float offY)
{ x += offX; y += offY; }
float getX() const { return x; }
float getY() const { return y; }
private: //私有数据成员
float x, y;
};
class Rectangle: private Point { //派生类定义部分
public: //新增公有函数成员
void initRectangle(float x, float y, float w, float h) {
initPoint(x, y); //调用基类公有成员函数
this->w = w;
this->h = h;
}
void move(float offX, float offY) { Point::move(offX, offY); }
float getX() const { return Point::getX(); }//这样就可以直接用派生类的对象直接访问。
float getY() const { return Point::getY(); }
float getH() const { return h; }
float getW() const { return w; }
private: //新增私有数据成员
float w, h;
};
int main() {
Rectangle rect; //定义Rectangle类的对象
rect.initRectangle(2, 3, 20, 10); //设置矩形的数据
rect.move(3,2); //移动矩形位置
cout << "The data of rect(x,y,w,h): " << endl;
//输出矩形的特征参数
cout << rect.getX() <<", " << rect.getY() << ", "<< rect.getW() << ", "<< rect.getH() << endl;
return 0;
}
保护继承(protected)
protected 成员的特点与作用
class A {
protected:
int x;
};
int main() {
A a;
a.x = 5; //错误
}
class A {
protected:
int x;
};
class B: public A{
public:void function();
};
void B:function() {
x = 5; //正确
}
在派生类 B 的成员函数 functÎon 内部,是完全可以访问基类的保护成员的。
==注意 ==如果 B 是 A 的派生类, B 的成员函数只能通过 B 的对象访问 A 中定义的protected 成员,而不能通过 A 的对象访问 A 的 protected 成员。
如果派生类有自己新增的成员,且需要通过构造函数初始化,则派生类要自定义构造函数。
若不完全继承基类的构造函数
//单继承
#include
using namespace std;
class B {
public:
B();
B(int i);
~B();
void print() const;
private:
int b;
};
B::B() {
b=0;
cout << "B's default constructor called." << endl;
}
B::B(int i) {
b=i;
cout << "B's constructor called." << endl;
}
B::~B() {
cout << "B's destructor called." << endl;
}
void B::print() const {
cout << b << endl;
}
class C: public B {
public:
C();
C(int i, int j);
~C();
void print() const;
private:
int c;
};
C::C() {
c = 0;
cout << "C's default constructor called." << endl;
}
C::C(int i,int j): B(i), c(j){
cout << "C's constructor called." << endl;
}
C::~C() {
cout << "C's destructor called." << endl;
}
void C::print() const {
B::print();
cout << c << endl;
}
int main() {
C obj(5, 6);
obj.print();
return 0;
}
多继承
多继承时,有多个直接基类,如果不继承基类的构造函数,派生类构造函数需要给所有基类构造函数传递参数。我们来看一下语法规定
派生类与基类的构造函数
多继承且有对象成员时派生的构造函数定义语法
派生类名::派生类名(形参表):
基类名1(参数), 基类名2(参数), …, 基类名n(参数),
本类成员(含对象成员)初始化列表
{
//其他初始化
};
构造函数的执行顺序
注意:构造函数初始化列表中基类名、对象名之间的次序无关紧要,它们各自出现的顺序可以是任意的,无论它们的顺序怎样安排,基类构造函数的调用和各个成员对象的初始化顺序都是确定的。
派生类构造函数举例(多继承、含有内嵌对象)。
这是一个具有一般性特征的例子,有 3 个基类 Basel , Base2 和 Base3 。其中 Base3 只有一个默认的构造函数,其余两个基类的成员只是一个带有参数的构造函数。类 Derived由这 3 个基类经过公有派生而来。派生类新增加了 3 个私有对象成员,分别是 Basel,Base2 和 Base3 类的对象。程序如下:
# include < iostream>
using namespace std;
class Basel {
public:
//基类 Basel ,构造函数有参数
Base1 (int i) {cout<< "Constructing Base1 "<< i<< endl; }
};
class Base2 (
public:
//基类 Base2 ,构造函数有参数
Base2( int j) {cout<< "Constructing Base2 "<< j<< endl; }
};
class Base3 {
public:
Base3 () {cout<< "Constructing Base3 铃 "<<endl; }
//基类 Base3 ,构造函数无参数
};
class Derived: public Base2, public Basel, public Base3 (
// 派生新类 Derived,注意基类名的顺序
public: //派生类的公有成员
Derived(int a ,工nt b, int c, int d) : Basel(a),member2(d),member1(c),Base2(b)
{}
//注意基类名的个数与顺序,注意成员对象名的个数与顺序
private:
Base1 member1;
Base2 member2;
Base3 member3;
int main () {
Derived obj (1 , 2, 3, 4);
return 0;
派生类的私有成员对象程序的运行结果为:
Constructing Base2 2
Constructing Basel 1
Constructing Base3 *
Constructing Basel 3
Constructing Base2 4
Constructing Base3'*
Derived 类构造函数的执行情况,它应该是先调用基类的构造函数,然后调用内嵌对象的构造函数。基类构造函数的调用顺序是按照派生类定义时的顺序,因此应该是先 Base2. 再 Base1. 最后 Base3; 而内嵌对象的构造函数调用顺序应该是按照成员在类中声明的顺序,应该是先 Base1 ,再 Base2 ,最后 Base3 .
派生类构造函数定义中,并没有显式列出基类 Base3 和 Base3 类的对象 member3 ,这时系统就会自动调用该类的默认构造函数。
析构函数与构造函数的执行顺序相反,由于时采用的了栈的方式,先进后出。
为了在派生类中使用和基类的同名成员,必须在该成员名前加上基类名和作用域标识符" : : ",
基类名 ::成员名
#include
using namespace std;
class Base1 {
public:
int var;
void fun() { cout << "Member of Base1" << endl; }
};
class Base2 {
public:
int var;
void fun() { cout << "Member of Base2" << endl; }
};
class Derived: public Base1, public Base2 {
public:
int var;
void fun() { cout << "Member of Derived" << endl; }
};
int main() {
Derived d;
Derived *p = &d;
//访问Derived类成员
d.var = 1;
d.fun();
//访问Base1基类成员
d.Base1::var = 2;
d.Base1::fun();
//访问Base2基类成员
p->Base2::var = 3;
p->Base2::fun();
return 0;
}
多继承时的二义性和冗余问题
#include
using namespace std;
class Base0 { //定义基类Base0
public:
int var0;
void fun0() { cout << "Member of Base0" << endl; }
};
class Base1: public Base0 { //定义派生类Base1public: //新增外部接口
int var1;
};
class Base2: public Base0 { //定义派生类Base2
public: //新增外部接口
int var2;
};
class Derived: public Base1, public Base2 {
public:
int var;
void fun()
{ cout << "Member of Derived" << endl; }
};
int main() { //程序主函数
Derived d;
d.Base1::var0 = 2;
d.Base1::fun0();
d.Base2::var0 = 3;
d.Base2::fun0();
return 0;
}
注意:
在第一级继承时就要将共同基类设计为虚基类。
#include
using namespace std;
class Base0 {
public:
int var0;
void fun0() { cout << "Member of Base0" << endl; }
};
class Base1: virtual public Base0 {
public:
int var1;
};
class Base2: virtual public Base0 {
public:
int var2;
};
class Derived: public Base1, public Base2 {
//定义派生类Derived
public:
int var;
void fun() {
cout << "Member of Derived" << endl;}
};
int main() {
Derived d;
d.var0 = 2; //直接访问虚基类的数据成员
d.fun0(); //直接访问虚基类的函数成员
return 0;
}
虚基类及其派生类构造函数
//虚基类时的构造函数举例
#include
using namespace std;
class Base0 {
public:
Base0(int var) : var0(var) { }
int var0;
void fun0() { cout << "Member of Base0" << endl; }
};
class Base1: virtual public Base0 {
public:
Base1(int var) : Base0(var) { }
int var1;
};
class Base2: virtual public Base0 {
public:
Base2(int var) : Base0(var) { }
int var2;
};
class Derived: public Base1, public Base2 {
public:
Derived(int var) : Base0(var), Base1(var), Base2(var)
{ }
int var;
void fun()
{ cout << "Member of Derived" << endl; }
};
int main() { //程序主函数
Derived d(1);
d.var0 = 2; //直接访问虚基类的数据成员
d.fun0(); //直接访问虚基类的函数成员
return 0;
}
重载为类成员的运算符函数定义形式
函数类型 operator 运算符(形参)
{
......
}
参数个数=原操作数个数-1 (后置++、--除外)
#include
using namespace std;
class Complex {
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
//运算符+重载成员函数
Complex operator + (const Complex &c2) const;
//运算符-重载成员函数
Complex operator - (const Complex &c2) const;
void display() const; //输出复数
private:
double real; //复数实部
double imag; //复数虚部
};
Complex Complex::operator+(const Complex &c2) const{
//创建一个临时无名对象作为返回值
return Complex(real+c2.real, imag+c2.imag);
}
Complex Complex::operator-(const Complex &c2) const{
//创建一个临时无名对象作为返回值
return Complex(real-c2.real, imag-c2.imag);
}
void Complex::display() const {
cout<<"("<<real<<", "<<imag<<")"<<endl;
}
int main() {
Complex c1(5, 4), c2(2, 10), c3;
cout << "c1 = "; c1.display();
cout << "c2 = "; c2.display();
c3 = c1 - c2; //使用重载运算符完成复数减法
cout << "c3 = c1 - c2 = "; c3.display();
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3 = c1 + c2 = "; c3.display();
return 0;
}
#include
using namespace std;class Clock {//时钟类定义
public:
Clock(int hour = 0, int minute = 0, int second = 0);
void showTime() const;
//前置单目运算符重载
Clock& operator ++ ();
//后置单目运算符重载
Clock operator ++ (int);
private:
int hour, minute, second;
};
Clock::Clock(int hour, int minute, int second) {
if (0 <= hour && hour < 24 && 0 <= minute && minute < 60
&& 0 <= second && second < 60) {
this->hour = hour;
this->minute = minute;
this->second = second;
} else
cout << "Time error!" << endl;
}
void Clock::showTime() const { //显示时间
cout << hour << ":" << minute << ":" << second << endl;
}
Clock & Clock::operator ++ () {
second++;
if (second >= 60) {
second -= 60; minute++;
}
if (minute >= 60) {
minute -= 60; hour = (hour + 1) % 24;
}return *this;
}
Clock Clock::operator ++ (int) {
//注意形参表中的整型参数
Clock old = *this;
++(*this); //调用前置“++”运算符
return old;
}
int main() {
Clock myClock(23, 59, 59);
cout << "First time output: ";
myClock.showTime();
cout << "Show myClock++: ";
(myClock++).showTime();
cout << "Show ++myClock: ";
(++myClock).showTime();
return 0;
}
后面的部分重要,浓缩不了知识点,直接看书会更好一些。
vector、stack、queue、map、set 这些在C++中统称为容器,这些大小可以用.size()获取。
vector 定义
:vector 遍历
:`:for (auto it = c.begin(); it != c.end(); it++) {
cout << *it << " "; // 使⽤迭代器的⽅式访问vector
}
for (int i = 0; i < c.size(); i++) {
cout << c[i] << " ";
}
在头文件==
map<char,int> mp;
mp['c'] = 20;
mp['c'] = 30;
printf("%d\n",mp['c']); // 结果为30
for(auto it = j.begin(); it!= m.end();it++){
cout<< it->fist<<" "<< it->second << endl;
}
// 访问map的第⼀个元素,输出它的键和值
cout << m.begin()->first << " " << m.begin()->second << endl;
// 访问map的最后⼀个元素,输出它的键和值
cout << m.rbegin()->first << " " << m.rbegin()->second << endl;
// 输出map的元素个数
cout << m.size() << endl;
map<char,int> mp;
mp['a'] = 1;
map <char,int> mp;
int main(){
mp['a'] = 1;
mp['b'] = 2;
auto it = mp.find('a'); //auto 可以让编译器根据初始值类型直接推断变量的类型
printf("%c %d\n",it->first,it->second);
mp.erase(it,mp.end());//删除it之后的所有映射。
mp.erase(it) //就把mp['a']删除了
mp.clear();
}
叮叮~具体应用可以参考本人写的文章:
:PAT1018因为是集合所以有着互异性,set里面的各元素是各不相同的,而且set会按照元素进行从小到大排序。
set
set
只能通过迭代器访问,例如:set
for(set \<int> ::interator it = s.begin();it!= s.end(
;it++){
printf("%d",*it)
}
或者,将set
for (int i = 0; i < 6; i++) {
s.insert(i); // 向集合s⾥⾯插⼊i
}
*(s.find(2)) // 如果2存在,则printf 结果为2。
s.find(2)!=s.end() // 判断该元素是否存在
头文件在
stack
这里的栈和数据结构中的栈一样,只能通过top()来访问栈顶元素
for (int i = 0; i < 6; i++) {
s.push(i); // 将元素i压⼊栈s中
}
while(s.size()!=0){
cout << s.top() << endl; // 访问s的栈顶元素
s.pop(); //弹出栈顶元素
} // 相当于遍历所有的元素,并且遍历完后s 中的元素会被清空。
头文件在
queue
这里的队列和数据结构中的栈一样,只能通过front()来访问队首元素,或者用back()来访问队尾元素。
queue<int> q; // 定义⼀个空队列q
for (int i = 0; i < 6; i++) {
q.push(i); // 将i的值依次压⼊队列q中
}
printf("%d %d\n",q.front(),q.back()); // 访问队列的队⾸元素和队尾元素 ,结果为1 5
cout << q.size() << endl; // 输出队列的元素个数
q.pop() //弹出首个元素
勉励
不要留给自己下一次再来的机会!(*╹▽╹*)
申明:
本文参考资料:
建议:读者如果有时间的话,最好自己码码。
如果觉得我的文章对你有所帮助与启发,点赞给我个鼓励吧(づ ̄3 ̄)づ╭❤~
关注我和我一起共勉加油
吧!
如果文章有错误,还望不吝指教!