C++基础2:类与对象

10 篇文章 0 订阅

C++基础2:类与对象

1、认识类与对象

1.1 什么是类

类(class)是类型(type),是用户自定义的类型。为什么不叫它type,因为借用Simula语言中的class关键字

1.2 为什么要有类

基于便理性的考虑,现实世界中物(object)通常被分为几种独立的分类。

1.3 基本概念

概念

比喻

对象/实例

楼房

实例化

建造

建筑图纸

1.4 面向对象四大特征

特征

说明

类比

抽象

抽出具体事物的普遍性的本质

分门别类:鸟类、哺乳类、鱼类

封装

把数据与处理(函数)包在一起

通讯录(增加、删除)

继承

数据与处理(函数)的传承

财富与绝技、混血儿(肤色/头发、两种语言)

多态

同一个事物(函数)的多种形态

手机键盘数字与字母、电脑键盘功能键

2、类的定义与对象创建

2.1 类的定义:与struct相似(C++)

class 类名{
    成员变量成员函数声明
};

class定义最后的;一定不能忘记

2.2 构成

(1)基本构成

构成

作用

数据成员(data member)/成员变量/属性

对象内部数据和状态,只能在类定义中声明,可以在成员函数中直接调用

成员函数/方法

对象相关的操作,可以在类内实现或者类外实现

(2)实例

复数complex

(3)成员函数实现与类定义分离

作用域运算符::函数归属

(4)访问限定符

限定符号

作用

private(默认)

私有

public

公开

protected

保护

实践中,成员变量多数情况使用private或者protected,成员函数多数使用pbulic。通常,通过成员函数改变对象的成员变量

(5)类定义与类实现分离

头文件声明
方式:#pragma once或者#ifnde…#endif
作用:防止头文件二次编译
源文件 – 实现
引用头文件:#include <>(标准库函数)/#include “”(自定义/第三方函数)
在C++书籍中为了方便.h与.cpp不做分离,但是项目开发中,需要分开。

对象做参数和返回值

Complex Add(Complex c1,Complex c2);
Complex Complex::Add(Complex c);

(6)class与struct区别

#include <iostream>

using namespace std;

struct Spos{
    int x,y,z;
};

class Cpos{
public:
    int x,y,z;
};

int main(){
#ifdef STRUCT
    Spos spos = {1,1,1};
    cout <<"sasdasd"<< "("<<spos.x << ","<<spos.y<< ","<<spos.z<<")"<<endl;
#else
    Cpos cpos={1,1,1};
    cout <<"cpos"<< "("<<cpos.x << ","<<cpos.y<< ","<<cpos.z<<")"<<endl;
#endif
}

(7)C++中class与struct区别

(1)默认的访问控制不同

struct是public,class是private
(2)初始化
struct可以使用花括号内的初始值列表{…}初始化,class不可以(C++98不可以,C++11可以)
(3)注意

C++的struct可以有成员函数,而C不可以。
C++的struct可以使用访问控制关键字(public private protected),而C不可以。


SPos spos; // C++
struct SPos spos; // C/C++

成员变量默认初始化为随机值(主要影响指针)。

2.3 对象创建/实例化

直接创建 – 类作为类型定义变量 – 栈上创建

(1)举例

基本类型:

int main(){
    int a = 10;
    int b(10);
    cout << a << " " << b << endl;
}

基本类型的初始化新增语法:

int a(0);// 等价 int a = 0;
const float b(1.0);// 等价 const float b = 1.0;

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo d; // 变量(命名对象)
    Demo(); // 匿名对象
}

基本语法

类名 对象名;  // 调用默认构造函数
类名(); // 创建匿名对象

(2)动态创建-堆上创建

例如
基本类型

int* p = new int;
delete p;
p = NULL;

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo* d = new Demo;
    delete d;
    d = NULL;
}

基本语法

类名* 对象指针 = new 类名;// 调用默认构造函数
delete 对象指针;

对象指针new可以为对象设置初始值,例如下面代码

int* p = new int(100);
cout << *p << endl;

(3)动态创建数组–堆上创建

基本类型

int* pa = new int[10];
delete pa;// 只释放p[0]
delete [] pa;// 释放全部数组

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo* d = new Demo[10];
    delete [] d;
    d = NULL;
}

对象数组指针new不可以为对象设置初始值。

int* pa = new int[10](100); // error: array 'new' cannot have initialization arguments 

注意:C++除了特殊情况,很少直接使用malloc()/free()申请释放内存,取而代之的是new/delete。

3、this指针

作用域:
类内部
特点:
(1)类的一个自动生成、自动隐藏的私有成员
(2)每个对象仅有一个this指针
(3)当一个对象被创建时,this指针就存放指向对象数据的首地址
(4)不是对象本身的一部分,不会影响sizeof(对象)的结果
如果成员函数形参与成员变量同名,使用this->做为前缀区分。

4、方法

4.1 构造函数

(1)语法

类名(参数){
    函数体
}

(2)特点

1、在对象被创建时自动执行
2、构造函数的函数名与类名相同
3、没有返回值类型、也没有返回值
4、可以有多个构造函数

(3)调用时机

对象直接定义创建–构造函数不能被显式调用
new动态创建

(4)默认构造函数

类中没有显式的定义任何构造函数,编译器就会自动为该类型生成默认构造函数,默认构造函数没有参数。

(5)构造函数的三个作用

1、给创建的对象建立一个标识符
2、为对象数据成员开辟内存空间
3、完成对象数据成员的初始化

4.2 初始化列表

(1)语法

类名(参数):成员变量(参数){
  函数体
}

(2)作用

初始化非静态成员变量

(3)说明

从概念上来讲,构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
必须使用初始化列表的情况
1、常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2、引用类型,引用必须在定义的时候初始化,并不能重新赋值,所以也要写在初始化列表里面
3、没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
初始化列表与构造函数内部成员变量赋值的区别
成员变量初始化与成员变量赋值
能使用初始化列表的时候尽量使用初始化列表

(4)成员变量的初始化顺序

#include <iostream>

using namespace std;

class Member1{
public:
    Member1(){
        cout << "Member1 Init" << endl;
    }
};
class Member2{
public:
    Member2(){
        cout << "Member2 Init" << endl;
    }
};
class Member3{
public:
    Member3(){
        cout << "Member3 Init" << endl;
    }
};
class Test{
public:
    Test():m3(),m2(),m1(){};
private:
    Member1 m1;
    Member2 m2;
    Member3 m3;
};

int main(){
    Test test;
}

(5)C++的函数可以增加默认参数。

写法:

1、默认参数必须写在函数声明中,不能写在函数实现的参数列表中
2、默认参数必须写在所有非默认参数的后面
3、默认参数可以写任意多个
使用:
1、默认参数可以不用传递值,此时,使用默认值
2、默认参数可以传值,此时,使用实参值

4.3 析构函数

问题:银行管理多个账户的内存泄露及解决方案

(1)语法

~类名(){
    函数体
}

注意:
1、析构函数的函数名与类名相同
2、函数名前必须有一个~
3、没有参数
4、没有返回值类型、也没有返回值
5、只能有一个析构函数

(2)调用时机

1、对象离开作用域
2、delete

(3)默认析构函数

类中没有显式的定义析构函数,编译器就会自动为该类型生成默认析构函数

(4)作用

释放对象所申请占有的资源

RAII(资源的取得就是初始化,Resource Acquisition Is Initialization)
C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。

(5)分析下面程序执行结果

#include <iostream>

using namespace std;

class Test{
public:
    Test(){
        cout << "Test Construct" << endl;
    }
    ~Test(){
        cout << "Test Deconstruct" << endl;
    }
};

int main(){
    //局部对象
    cout << "Before {" <<endl;
    {
        cout << "After {" << endl;
	Test t;
	cout << "Before }" << endl;
    }
    cout << "After }" << endl;
    
    //动态对象
    cout << "Before {" << endl;
    {
        cout << "After {" << endl;
	Test *pt = new Test;
	delete pt;
	pt = NULL;
	cout << "Before }" << endl;
    }
    cout << "After }" << endl;
    return 0;
}

输出结果:

Before {
After {
Test Construct
Before }
Test Deconstruct
After }
Before {
After {
Test Construct
Test Deconstruct
Before }
After }

4.4 引用(别名)

(1)语法

声明:const 类型名& 对象名/类型名& 对象名
使用:与对象变量、基本类型变量一样

例如:

int a = 1
int& b = a;
cout << "&a:" << &a << endl;
cout << "&b:" << &b << endl;
b = 13;
cout << a << endl;

引用其实就是一个别名,a与b代表的是相同的对象。

(2)何处使用引用

1、函数参数列表
2、函数返回值
3、成员变量–对象初始化时,必须显示初始化的变量

(3)为何使用引用

1、避免对象复制
2、避免传递空指针
3、使用方便

类型:
1、基本类型(bool、char、short、float)
2、复合类性(指针、数组、引用)
3、自定义类型(struct、union、class)

(4)引用作用

取代指针

#include <iostream>
using namespace std;

void Func(int *n){
    *n = 2;
}

void Func(int & n){
    n=3;
    cout << &n << endl;
}

int main(){
    int a =1;
    cout << &a << endl;
    Func(&a);
    cout << a << endl;
    Func(a);
    cout << a << endl;
}

输出结果:

0x7ffdeb40f64c
2
0x7ffdeb40f64c
3

函数三种传参方式:
1、传值void Func(int n)
2、传地址/指针void Func(int *n)
3、传引用void Func(int &n)

(5)引用与指针的区别

1、指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名
2、引用只能在定义时被初始化一次,之后不可变;指针可变
3、引用不能为空,指针可以为空
4、引用使用时无需解引用*,指针需要解引用
5、sizeof引用得到的是所指向的变量/对象的大小,而sizeof指针得到的是指针本身的大小
6、对于引用类型的成员变量,所属类的大小时按照指针大小计算,自身大小按照自身类型计算
引用通常适用于三种情形:成员变量和函数的参数列表以及函数返回值

4.5 拷贝/复制构造函数

(1)语法

类名(类名& 形参){ 
    函数体
}

或者

类名(const 类名& 形参){
    函数体
}

(2)调用时机

手动调用

类名 对象名;  // 调用默认构造函数
类名 对象2 = 对象1;    // 调用复制构造函数
类名 对象3(对象1);     // 调用复制构造函数

自动调用

1、一个对象作为函数参数,以值传递的方式传入函数体
2、一个对象作为函数返回值,以值从函数返回
3、 一个对象拷贝构造,它的成员对象自动调用拷贝构造
4、子对象拷贝构造父对象自动拷贝构造

实践说明:

1、gcc/clang自动RVO/NRVO优化,不执行拷贝构造函数。可以在编译命令添加选项禁止-fno-elide-constructors;
2、VC在Debug环境下返回值执行拷贝构造函数,在Release环境下实施RVO/NRVO优化。

4.6 默认拷贝构造函数

本质:内存拷贝
作用:复制一个已经存在的对象

(1)运行结果

#include <iostream>
#include <string>

using namespace std;

class Test{
public:
    Test(){}
    Test(Test &t){cout << "Test(Test &t)" << endl;}
    Test(Test const &t){cout << "Test(Test const &t)"<<endl;}
};

int main(){
    Test t1;
    Test t2;
    const Test t3;
    Test t4 = t3;
}

运行结果:

Test(Test const &t)

5、赋值运算符重载函数

(1)语法

类名& operater=(const 类名& 形参){
  // 赋值操作
  return *this;
}

(2)调用时机

赋值

(3)默认赋值运算符重载函数

内存拷贝

(4)作用

赋值

(5)拷贝构造函数与赋值操作符的区别

1、拷贝构造函数:用一个已经存在的对象来初始化一个未曾存在的对象
2、赋值操作符:当两个对象都已经存在

Demo a;
Demo b;
b = a;
Demo c = a;
Demo d = Demo(a);

6、深拷贝(Memberwise Copy)与浅拷贝(Bitwise Copy)

浅拷贝:编译器默认生成的类实例间拷贝行为,对带有指针的类来说会引发memory leak
深拷贝:用户定义的行为(实质是一种构造函数)

(1)问题

一个类中存在指针类型的成员变量。并且类构造和析构管理指针的申请与释放

class Demo{
public:
    Demo(int n){
        p = new int(n);
    }
    ~Demo(){
        delete p;
        p = NULL;
    }
private:
    int* p;
};

调用拷贝构造函数会出现什么情况?

Demo a(10);
Demo b = a;

(2)区别

浅拷贝:只拷贝指针地址
深拷贝:重新分配堆内存,拷贝指针指向内容

(3)最佳实践

三大定律(Rule of three/the Law of The Big Three/The Big Three)
如果类中明确定义下列其中一个成员函数,那么必须连同其他两个成员函数编写至类内,即下列三个成员函数缺一不可。

析构函数(destructor)
复制构造函数(copy constructor)
复制赋值运算符(copy assignment operator)

7、友元

(1)作用

非成员函数访问类中的私有成员

(2)分类

1、全局友元函数:将全局函数声明成友元函数
2、友元成员函数:类的提前引用声明,将一个函数声明位多个类的友元函数
3、友元类:将整个类声明为友元

(3)特点

1、友元关系单向性
2、友元关系不可传递

8、const限定符

(1)本质

只读(read only)

(2)const与变量/对象

const 类型 变量 = 初始值;
const 类型 对象;

例如:

const int size = 4;
比较前卫的写法

类型 const 变量 = 初始值;
类型 const 对象;

例如:

int const size = 4;

(3)注意

1、定义时必须初始化
2、全局作用域声明的const变量默认作用域是定义所在文件
3、const对象只能调用const成员函数

(4)const与#define的区别

const

#define

编译器处理方式

编译运行阶段使用

预处理阶段展开/替换

类型

有具体的类型

没有类型

安全检查

编译阶段会执行类型检查

不做任何类型检查

存储方式

分配内存

不分配内存

(5)const与指针

No.

类型

语法

作用

1

const指针

类型 * const 变量 = 初始值

指针指向地址不能改变

2

指向const对象的指针

const 类型*变量= 初始值;类型 const * 变量 = 初始值

指针指向对象不能改变

3

指向const对象的const指针

const 类型* const 变量 = 初始值

指针指向地址和对象不能改变

(6)const与引用

类型 const &变量 = 初始值;与const 类型& 变量 = 初始值;都是引用对象不能改变。

(7)const与函数的参数和返回值

类型

语法

作用

说明

const参数

返回值类型 函数(const 类型 形参)

函数内部不能改变参数的值

这样的参数的输出值

const 返回值

const 返回值类型 函数(形参列表)

函数的返回值不能改变

常用于字符串/指针

(8)const成员变量

1、不能在类声明中初始化const对象(C++11可以)
2、const成员变量只能在类构造函数的初始化列表中初始化

class 类名{
public:
     类名(类型 形参):成员变量(形参){}
private:
     const 类型 成员变量;
}

3、应用:
const成员变量一般用于类定义后不可修改的信息,例如:学生学号
4、注意:
(1)使用const成员变量不能省略构造函数(引用类型的成员变量相同)
(2)使用const成员变量不能使用赋值运算符重载函数

(9)const成员函数

成员函数不能修改类中任何成员变量,一般写在成员函数的最后来修饰
1、声明

class 类名{
public:
    返回值类型 函数名(形参列表)const;
}

2、定义

返回值类型 函数名(形参列表)const;

3、示例

#include <iostream>

using namespace std;

class Test{
public:
    void Print() const {
        cout << "Test" << endl;
    }
};

void Func(const Test &t){
    t.Print();
}

int main(){
    Test t;
    Func(t);//变量
    Func(Test());//匿名对象
    const Test t2;
    Func(t2);
    t2.Print();
}

必须在成员函数的声明和定义后都加上const

const修饰位置

作用

变量

变量不可修改,通常用来替代#difine

对象/实例

对象的成员变量不可修改,只能调用const成员函数

函数参数

参数不能在函数内部修改,只作为入参

函数返回值

返回的结果不能被修改,常用于字符串

成员变量

只能在初始化列表中初始化

成员函数

不改变成员变量

只要能够使用const,尽量使用const

9、static限定符

(1)本质

1、生存周期:整个程序的生存周期
2、作用域:属于类,不属于对象

(2)语法

1、声明

class 类名{
    static 返回类型 函数(形参列表);
};

2、定义

返回类型 类名::函数(形参列表){
    函数体;
}

3、调用
通过类名调用(Class Name)

类名::函数(实参列表);

通过对象调用(object)

对象.函数(实参列表);

(3)规则

1、static只能用于类的声明中,定义不能标示为static
2、非静态是可以访问静态的方法和函数
3、静态成员函数可以设置private,public,protected访问权限

(4)禁忌

1、静态成员函数不能访问非静态函数或者变量
2、静态成员函数不能使用this关键字
3、静态成员函数不能使用cv限定符(const与volatile)
因为静态成员函数是属于类而不是某个对象
volatile是一个不常用的关键字,作用是改善编译器的优化能力

10、静态成员变量

(1)语法

1、在类定义中声明,但是在类实现中初始化
2、在声明时需要指定关键字static,但是在类外定义时不要指定static
3、对象的大小不包含静态成员变量
因为静态成员变量是属于类而不是某个对象。静态成员变量所有类的对象/实例共享。

static修饰位置

作用

变量

静态变量

函数

只源文件内部使用的函数

成员变量

对象共享变量

成员函数

类提供的函数,或者作为静态成员对象的接口

单例模式:使用静态成员变量和静态成员函数

(2)习题

下面代码输出的结果

#include <iostream>

using namespace std;

class Test{
public:
    static char x;
};

char Test::x='a';
int main(){
    Test exp1,exp2;
    cout << exp1.x << " ";
    exp1.x+=5;
    cout << exp2.x << endl;
}

输出结果:

a f

11、const static限定符

#include <iostream>

using namespace std;

class StaticConstTest{
public:
    void print(){
        cout << test1 << " " << test2 << endl;
    }
private:
    static const int test =1;
    static const int test2;
};

static const int StaticConstTest::test2=2;

int main(){
    StaticConstTest sct;
    sct.print();
}

变量类型

声明位置

一般成员变量

在构造函数初始化列表中初始化

const成员变量

必须在构造函数初始化列表中初始化

static成员变量

必须在类外初始化

static const/const static成员变量

变量声明处或者类外初始化

注意:
static const/const static成员变量在类初始化必须是数字类型

12、内联函数

inline —宏定义的接班人

在这里插入图片描述

(1)条件

一般用在代码比较简单的函数

(2)语法

1、关键字inline必须与函数实现/定义体放在一起i才能使函数称为内联,将inline放在汉所属声明前不起任何作用
2、定义在类声明之中的成员函数将自动地称为内联函数
3、通常内联函数定义在头文件中

(3)慎用内联

1、如果函数体内的代码比较长,使用内联将导致内存消耗代价较高
2、如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大
3、不要随便地将构造函数和析构函数的定义体放在类声明中

(4)本质

内联函数的代码直接替换函数调用,省去函数调用的开销

13、运算重载符

13.1 语法

运算符重载主要有两种方式实现:
1、成员函数运算符重载

返回值类型 operator 运算符(参数){
      函数体
}

2、友元函数运算符重载

friend 返回值类型 operator 运算符(形参列表) { 
      函数体 
} 

13.2 说明

(1)内存运算符

1、成员函数

void *operator new(size_t size);
void *operator new[](size_t size);
void operator delete(void*p);
void operator delete [](void* p);

2、友元函数

void *operator new(类名,size_t size);
void *operator new[](类名&,size_t size);
void operator delete(类名&,void*p);
void operator delete [](类名&,void* p);

(2)流运算符

流运算符只能使用友元函数实现

inline ostream &operator << (ostream&, 类名&)
inline istream &operator >> (istream&, 类名&)

(3)类型转换符

这些运算符只能使用成员函数实现

operator char* () const;
operator int ();
operator const char () const;
operator short int () const;
operator long long () const;

(4)其他运算符重载

这些运算符只能使用成员函数实现。

类名& operator = (const 类名& );
char operator [] (int i);//返回值不能作为左值
const char* operator () ();
T operator -> ();

13.3 规则

1、不能重载的运算符:成员运算符.、作用域运算符::、sizeof、条件运算符?:
2、不允许用户自定义新的运算符,只能堆已有的运算符进行重载
3、重载运算符不允许改变运算符原操作数的个数
4、重载运算符不能改变运算符的优先级
5、重载运算符函数不唔唔有默认的参数,会导致参数个数不匹配

13.4 本质及练习

(1)本质

函数重载

(2)练习

1、重载运算符

#include <iostream>
#include <algorithm>
#include <sstream>

using namespace std;

string operator+(int n,string const& str){
    ostringstream oss;
    oss << n << str;
    return oss.str();
}

string operator+(string const& str,int n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
}
string operator+(float n ,string const& str){
    ostringstream oss;
    oss << str << n;
    return oss.str();
}
string operator+(string const& str,float n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
}
string operator+(double n,string const& str){
    ostringstream oss;
    oss << str << n;
    return oss.str();
}
string operator+(string const& str,double n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
}

int main(){
    string a = "test";
    string res1 = "str" + a;
    cout << res1 << endl;

    auto res2 = 123 +a;
    cout << res2 << endl;
    auto res3 = a + 123;
    cout << res3 << endl;

    auto res4 = 123.456f+a;
    cout << res4 << endl;
    auto res5 = a + 123.456f;
    cout << res5 << endl;

    auto res6 = 123.456+a;
    cout << res6 << endl;
    auto res7 = a + 123.456;
    cout << res7 << endl;
    
}

运行结果:

strtest
123test
test123
test123.456
test123.456
test123.456
test123.456

2、如下定义模板,已知a,b是A的两个对象,则下列表达式中错误的是:

class A{
    int n;
public:
    A(int i):n(i) {}
    A operator+(A b){
        return A(n + b.n);
    }
};

14、练习

1、类的简单使用

#include <iostream>
#include <cmath>

using namespace std;

class Point{
public:
    int x;
    int y;
    int z;
    void Print(){
        cout << "(" << this->x <<","<< this->y<<","<<this->z<<")"<<endl;
    }
};

class Line{
public:
    Point p1;
    Point p2;
    float GetLength(){
        return sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2)+pow(p1.z-p2.z,2));
    }
    bool IsContain(Point p){
        float a = 1.0 *(p1.x-p2.x)/(p1.x-p.x);
	float b = 1.0 * (p1.y-p2.y)/(p1.y-p.y);
	float c = 1.0 *(p1.z-p2.z)/(p1.z-p.z);
	return abs(a-b)<1e-6 && (b-c)<1e-6;
    }
    void Print(){
        p1.Print();
	p2.Print();
    }
};

void PrintPoint(Point *p){
    cout << "(" << p->x <<","<< p->y<<","<<p->z<<")"<<endl;
}

int main(){
    Point p;
    p.x =1;
    p.y = 2;
    p.z=3;
    PrintPoint(&p);
    p.Print();

    Point p2;
    p2.x=3;
    p2.y=4;
    p2.z=5;

    Line line;
    line.p1 =p;
    line.p2=p2;
    line.Print();
    cout << line.GetLength() << endl;

    Point p3 = {1,3,5};

    Point orign = {0,0,0};
    Point p4={3,3,3};
    Point p5 = {4,4,4};
    Line line1 = {p4,p5};
    cout << line1.IsContain(orign) << endl;
}

2、 类对象的返回

#include <iostream>

using namespace std;

class Complex{
public:
    int real;
    int imag;
    Complex Add(Complex c){
       Complex res;
       res.real = real + c.real;
       res.imag = imag +c.imag;
       return res;
    }
    Complex Substract(Complex c){
        Complex res;
	res.real=real-c.real;
	res.imag=imag - c.imag;
	return res;
    }
    void Print(){
        cout << real;
	if(imag > 0 ) cout << "+";
	if(imag!=0) cout<<imag<<"i";
	cout << endl;
    }
};

int main(){
    Complex c = {2,3};
    c.Print();

    Complex c1 = {5,6};
    c1.Print();

    Complex c2 = c.Add(c1);
    c2.Print();

    Complex c3 = c.Substract(c1);
    c3.Print();

    Complex c4 = c3.Add(c);
    c4.Print();

}

3、一个账单

3.1 普通定义的数组和字符串数组

#include <iostream>
using namespace std;

class Bill{
public:
    char name[20];
    int count;
    float price;
    float GetTotal(){
        return count*price;
    }
};

class Account{
public:
    Bill *bills;
    size_t count;
    void Init(){
        bills = NULL;
	count = 0;
    }

    void Add(Bill bill){
        ++count;
	bills = (Bill *)realloc(bills,(count +1)*sizeof(Bill));
	bills[count -1]=bill;
    }

    void Print(){
        float sum = 0;
	cout << "name	count	price	total" << endl;
        cout << "-------------------------" << endl;
	for(int i=0;i<count;i++){
	    cout << bills[i].name << "	";
	    cout << bills[i].count << "	";
	    cout << bills[i].price << "	";
	    cout << bills[i].GetTotal() << "	" << endl;
	    sum+=bills[i].GetTotal();
	}
	cout << "----------------" <<endl;
	cout << "Total:			" << sum << endl;
    }

    void Destory(){
        free(bills);
	bills = NULL;
	count = 0;
    }
};


int main(){
    Account account;
    account.Init();
    Bill bills[] = {
        {"Apple",10,2.5},
	{"Orange",5,3},
	{"Pair",4,3.6}
    };
    for(int i=0;i<3;i++){
        account.Add(bills[i]);
    }
    account.Print();
    account.Destory();
}

3.2 使用string和vector

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Bill{
public:
    string name;
    //char name[20];
    int count;
    float price;
    float GetTotal(){
        return count*price;
    }
};

class Account{
public:
    vector<Bill> bills;
    /*Bill *bills;
    size_t count;
    void Init(){
        bills = NULL;
	count = 0;
    }*/

    void Add(Bill bill){
	bills.push_back(bill);
    }

    void Print(){
        float sum = 0;
	cout << "name	count	price	total" << endl;
        cout << "-------------------------" << endl;
	for(int i=0;i<bills.size();i++){
	    cout << bills[i].name << "	";
	    cout << bills[i].count << "	";
	    cout << bills[i].price << "	";
	    cout << bills[i].GetTotal() << "	" << endl;
	    sum+=bills[i].GetTotal();
	}
	cout << "----------------" <<endl;
	cout << "Total:			" << sum << endl;
    }

};


int main(){
    Account account;
    Bill bills[] = {
        {"Apple",10,2.5},
	{"Orange",5,3},
	{"Pair",4,3.6}
    };
    for(int i=0;i<3;i++){
        account.Add(bills[i]);
    }
    account.Print();
}

3.3对类进行封装

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Bill{
private:
    string name;
    int count;
    float price;
public:
    void Init(string n,int c,float p){
        name = n;
	count = c;
	price = p;
    }
    string GetName(){return name;}
    int GetCount(){return count;}
    float GetPrice(){return price;}
    float GetTotal(){return count * price;}
};


class Account{
private:
    vector<Bill> bills;
public:
    void Add(Bill bill){
        bills.push_back(bill);
    }
    void Print(){
	float sum=0;
        cout << "name	count	price	total" <<endl;
	cout <<"-----------------------------"<<endl;
	for(int i=0;i<bills.size();++i){
	    cout << bills[i].GetName() << "	";
	    cout << bills[i].GetCount() << "	";
	    cout << bills[i].GetPrice() << "	";
	    cout << bills[i].GetTotal() << "	"<<endl;
	    sum+=bills[i].GetTotal();
	}
	cout << "-----------------------"<<endl;
	cout<<"Total:			"<<sum<<endl;
    }
};

int main(){
    Account account;
    Bill bills[3];//创建三个类的数组
    bills[0].Init("Apple",10,2.5);
    bills[1].Init("Orange",5,3);
    bills[2].Init("Pair",4,3.6);//初始化三个类
    for(int i=0;i<3;i++){
        account.Add(bills[i]);//添加类到account类
    }
    account.Print();
}

3.4 账单的拆分

a、bill.cpp
#include <iostream>
#include "Bill.h"
#include "Account.h"
using namespace std;


int main(){
    Account account;
    Bill bills[3];
    bills[0].Init("Apple",10,2.5);
    bills[1].Init("Orange",5,3);
    bills[2].Init("Pair",4,3.6);
    for(int i=0;i<3;i++){
        account.Add(bills[i]);
    }
    account.Print();
}
b、Bill.h
#pragma once
#include <string>

using namespace std;

class Bill{
private:
    string name;
    //char name[20];
    int count;
    float price;
public:
    void Init(string n,int c,float p);
    string GetName();
    int GetCount();
    float GetPrice();
    float GetTotal();
};
c、Bill.cpp
#include "Bill.h"

string Bill::GetName(){return name;}
int Bill::GetCount(){return count;}
float Bill::GetPrice(){return price;}
float Bill::GetTotal(){
    return count*price;
}

void Bill::Init(string n,int c,float p){//::表示这个是Bill类的函数
        name =n;
	count =c;
	price=p;
    }
d、Account.h
#include <vector>
#include "Bill.h"
using namespace std;

class Account{
private:
    vector<Bill> bills;
public:
    void Add(Bill bill);
    void Print();
};
e、Account.cpp
#include <iostream>
#include "Account.h"
#include "Bill.h"

void Account::Add(Bill bill){
    bills.push_back(bill);
}
void Account::Print(){
    float sum=0;
    cout << "name	count	price	total" <<endl;
    cout <<"-----------------------------"<<endl;
    for(int i=0;i<bills.size();++i){
	cout << bills[i].GetName() << "	";
	cout << bills[i].GetCount() << "	";
	cout << bills[i].GetPrice() << "	";
	cout << bills[i].GetTotal() << "	"<<endl;
	sum+=bills[i].GetTotal();
    }
    cout << "-----------------------"<<endl;
    cout<<"Total:			"<<sum<<endl;
}

4、vector

#include <iostream>
#include <vector>

using namespace std;

int main(){
    vector<int> vec;
    int n;
    while(cin >> n){
        vec.push_back(n);
    }

    for(int i=0;i<vec.size();i++){
        cout << vec[i] << " ";
    }

    while(!vec.empty()){
        cout << vec.back() << " ";
	vec.pop_back();
    }

    cout << endl;
    vector<int> a = {1,2,3,4,5};
    vector<int> b = {6,7,8,9,10};

    vector<int> t =a;
    a=b;
    b=t;
}

运行结果:

1
2
3
4
5
1 2 3 4 5 5 4 3 2 1 

5、string

#include <iostream>
#include <string>

using namespace std;

int main(){
    string s = "Hello";
    cout << s << endl;

    cin >> s;
    cout << s << endl;
    cout << s.size()<<endl;

    for(int i=0;i<s.size();i++){
        cout << s[i] <<" ";
    }
    cout << endl;

    s = s + "12345678";
    cout << s << endl;

    s = "abcd";
    cout << s << endl;

}

运行结果:

Hello
qwe
qwe
3
q w e 
qwe12345678
abcd

6、this的使用

#include <iostream>

using namespace std;

class Simple{
    int n;
public:
    void SetN(int n){
        this->n=n;
    }
    void Print(){
        cout << n << endl;
    }
};

int main(){
    Simple s;
    s.SetN(101);
    s.Print();
}

7、构造函数修改的账单拆分

(1) bill.cpp

#include <iostream>
#include "Bill.h"
#include "Account.h"

using namespace std;

int main(){
    Account account;
    Bill a("Apple",10,2.5);
    Bill b("Orange",5,3);
    Bill c("Pair",4,3.6);
    account.Add(a);

    account.Add(b);
    account.Add(c);
    account.Print();
}

(2) Account.cpp

#include <iostream>
#include "Account.h"
#include "Bill.h"

void Account::Add(Bill bill){
    bills.push_back(bill);
}
void Account::Print(){
    float sum=0;
    cout << "name	count	price	total" <<endl;
    cout <<"-----------------------------"<<endl;
    for(int i=0;i<bills.size();++i){
	cout << bills[i].GetName() << "	";
	cout << bills[i].GetCount() << "	";
	cout << bills[i].GetPrice() << "	";
	cout << bills[i].GetTotal() << "	"<<endl;
	sum+=bills[i].GetTotal();
    }
    cout << "-----------------------"<<endl;
    cout<<"Total:			"<<sum<<endl;
}

(3) Account.h

#include
#include “Bill.h”

using namespace std;

class Account{
private:
vector bills;
public:
void Add(Bill bill);
void Print();
};

(4) Bill.cpp

#include "Bill.h"

string Bill::GetName(){return name;}
int Bill::GetCount(){return count;}
float Bill::GetPrice(){return price;}
float Bill::GetTotal(){
    return count*price;
}

Bill::Bill(string n,int c,float p){
    name = n;
    count = c;
    price = p;
}

(5) Bill.h

#pragma once
#include <string>

using namespace std;

class Bill{
private:
   string name;
   int count;
   float price;
public:
   Bill(string n,int c,float p);//构造函数
   void Init(string n,int c,float p);
   string GetName();
   int GetCount();
   float GetPrice();
   float GetTotal();
};

8、初始化列表

#include <iostream>

using namespace std;

class Simple{
public:
    Simple(int n){
        cout << "Simple(" << n << ")" << endl;
    }
};

class SimpleWrapper{
    Simple s;
public:
    SimpleWrapper(int n):s(n){
        s=Simple(n);
    }
};

int main(){
    Simple s(10);
    Simple arr[3] = {Simple(1),Simple(2),Simple(3)};
    SimpleWrapper sw(100);
}

总结

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于 java开发 的学习思路及方向。从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的 点击我的Gitee获取
还有 高级java全套视频教程 java进阶架构师 视频+资料+代码+面试题!

全方面的java进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值