C++入门速览

1.面向对象(OO)的思想特点:

1.1.封装--将属性和方法结合成独立的单位,隐藏对象的内部细节;

1.2.抽象--相当于封装对外的接口;类--比如人

1.3.继承--子类对基类的属性和方法的继承;

1.4.多态--子类集成基类后,可以有不同的表现形式;比如动物的运动:狗四条腿跑;袋鼠两条腿跑。

2.数组名是数组的首地址

#include<iostream>

using namespace std;

int AddArray(int *array, int n);

int main()
{
    int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int size = sizeof(data) / sizeof(data[0]);

    cout << "结果是:" << AddArray(data, size) << endl;

    return 0;
}

int AddArray(int *array, int n)
{
    int sum = 0;
    int i;

    for(i=0;i<n;i++)
    {
        sum += *array++;
    }
    return sum;
}

3.点操作符在C语言中用于结构中,而C++中的类相当于对结构和接口的封装;所以有:

cin.peek();

完整示例代码:

#include<iostream>

using namespace std;

int main()
{
    int sum = 0;
    cout << "请输入一串整数和任意数目的空格:";

    int i;  // C++允许在变量使用前的任意位置声明变量,C必须在开头
    while (cin >> i) //输入操作符一次从cin中提取一个整数 
    {
        sum += i;
        while (cin.peek()==' ') //省略空格
        {
            cin.get(); //取出字符,并将指针指向下个字符
        }
        if (cin.peek()=='\n') //peek只查看,不取出
        {
            break;
        }
        
        
    }
    cout << "结果是:" << sum << endl;
    return 0;
}

4.检查用户输入是否输入预期内容,用switch、case:

 注意:switch语句中表达式类型只能是整型或者字符型!!

注意:case里不要忘记添加break;

优势:与if语句相比,对于多条件判断,switch结构清晰,执行效率高。

缺点:不能判断区间。

// switch语句表达式为字符型
switch (typeIn)
    {
    case 'C':
    case 'c':
        tempOut = tempIn * RATIO +  ADD_SUBTRACT;
        typeOut = 'F';
        typeIn = 'C';
        break;

    case 'F':
    case 'f':
        tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
        typeOut = 'C';
        typeIn = 'F';
        break;
    
    default:
        typeOut = 'E';
        break;
    }

//switch表达式为整型
int main()
{
    int score = 0;
    
    cout << "请打分: " ;
    cin >> score;

    switch(score)
    {
        case 10:
        {
            cout << "评价为经典"  << endl;
            break;
        }
        case 9:
        {
            cout << "评价为经典"  << endl;
            break;
        }
        case 8:
        {
            cout << "评价为非常好"  << endl;
            break;
        }
        case 7:
        {
            cout << "评价为非常好"  << endl;
            break;
        }
        case 6:
        {
            cout << "评价为一般"  << endl;
            break;
        }

        default:
            cout << "评价为烂片"  << endl;
            break;
        
    }
    
    return 0;
}

5.函数的重载

        重载目的:方便对不同数据类型进行相同的处理。

        不要在程序中重载函数太多,同时作好注释。

        函数的重载多和模板一块使用,方便程序的简化,只需将参数的类型模板化即可。

        注意:重载函数需要均定义一遍,因为输入参数不同的重载函数本质上不是同一个函数!!

 重载实例:

/*
    C++函数重载实例,要旨如下:
    1.函数名称一样;
    2.函数的参数个数不一样,或者函数的参数类型不一样;
    3.重载函数一样需要提前定义,虽然函数名称一样,但本质上不算同一个函数。
    
    C++函数重载的作用:简化编程工作、提高代码可读性。
*/

#include<iostream>

void converTemperture(double tempIn, char typeIn);
void converTemperture(int tempIn, char typeIn); //函数重载的时候定义也要再写一次,因为函数输入不同,实际上相当于两个函数

int main()
{
    double tempIn;
    int tempInInt;
    char typeIn; 

    std::cout << "请以【xx.x C】或者【xx.x F】格式输入一个温度:";
    std::cin >> tempIn >> typeIn;
    std::cin.ignore(100, '\n'); //清除键盘缓冲区
    std::cout << "\n";

    converTemperture(tempIn, typeIn);

    std::cout << "请以【xx C】或者【xx F】格式输入一个温度:";
    std::cin >> tempInInt >> typeIn;
    std::cin.ignore(100, '\n'); //清除键盘缓冲区
    std::cout << "\n";

    converTemperture(tempInInt, typeIn);

    return 0;
}

void converTemperture(double tempIn, char typeIn)
{
    const unsigned short ADD_SUBTRACT = 32;
    const double RATIO = 9.0 / 5.0;

    double tempOut;
    char typeOut;

    switch (typeIn)
    {
    case 'C':
    case 'c':
        tempOut = tempIn * RATIO +  ADD_SUBTRACT;
        typeOut = 'F';
        typeIn = 'C';
        break;

    case 'F':
    case 'f':
        tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
        typeOut = 'C';
        typeIn = 'F';
        break;
    
    default:
        typeOut = 'E';
        break;
    }
    if(typeOut != 'E')
    {
        std::cout << tempIn << typeIn
            << " = " << tempOut
            << typeOut << "\n\n";
    }  
    else
    {
        std::cout << "输入错误" << "\n\n";

    }
    std::cout << "请输入任意字符结束程序!" << "\n\n";
    std::cin.get();
}

void converTemperture(int tempIn, char typeIn)
{
    const unsigned short ADD_SUBTRACT = 32;
    const double RATIO = 9.0 / 5.0;

    int tempOut;
    char typeOut;

    switch (typeIn)
    {
    case 'C':
    case 'c':
        tempOut = tempIn * RATIO +  ADD_SUBTRACT;
        typeOut = 'F';
        typeIn = 'C';
        break;

    case 'F':
    case 'f':
        tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
        typeOut = 'C';
        typeIn = 'F';
        break;
    
    default:
        typeOut = 'E';
        break;
    }
    if(typeOut != 'E')
    {
        std::cout << tempIn << typeIn
            << " = " << tempOut
            << typeOut << "\n\n";
    }  
    else
    {
        std::cout << "输入错误" << "\n\n";

    }
    std::cout << "请输入任意字符结束程序!" << "\n\n";
    std::cin.get();
}

 6.数据类型--数组、指针、结构

6.1数组的优点:可以把许多个同类型的值存储在同一个变量名下。

数组是由连续的内存位置组成的。

int aa[10]; //定义可存储10个整形数据的数组
#include<iostream>

#define ITEM 5 //方便修改数组的大小,不用去代码里一一修正
int main()
{
    int num[ITEM];

    std::cout << "请输入" << ITEM << "个整型数据!\n\n";

    for(int i=0; i<ITEM; i++)
    {
        std::cout << "请输入第" << i+1 <<"个数据:";
        while(!(std::cin >> num[i]))   //检验数据输入准确性
        {
            std::cin.clear(); //清空数据输入缓冲区
            std::cin.ignore(100, '\n');
            std::cout << "请输入一个合法的值";
        }
    }

    int total = 0; //局部变量需要初始化

    for(int j=0; j<ITEM; j++)
    {
        total += num[j];

    }

    std::cout << "总和是:" << total << "\n";
    std::cout << "平均值是:" << (float)total / ITEM;

    return 0;
}

6.2 字符串数组:

std::string
#include<iostream>
#include<string>

int main()
{
    std::string str;
    std::cout << "请输入一个字符串:";

    std::getline(std::cin, str); //getline可以获取所有的字符,std::in不行,会在空格出断掉
    std::cout << str <<"\n";

    return 0;
}

6.3 数组的数据类型可以是任意形式的,可以为指针形式的 ,也可以是对象形式的。

7.指针

指针的作用可以理解为走后门的媒介,别人通过指针走后门。

函数内部声明的变量为局部变量,作用域在这个函数内部,但指针可以在函数外部对该局部变量进行更改。

7.1对于变量索引的两种方法:

1)变量名;

2)地址。

7.2指针变量前边的类型是用来说明指针指向的数据的类型,请务必匹配使用。例如下面的代码,定义的指针*p是int型,则数据pp也得是int型。

int *p;
int pp = 132;
p = &pp;

7.3 允许void类型指针变量,指针只是存放地址

注意:对一个无类型指针进行解引用前,必须先把它转换为一种适当的数据类型

void *p;

7.4 *号的两种用途:

1)用于创建指针

a = 123;

int *pointer = &a;

2)对指针进行解引用

*pointer = 123;

8.数组和指针

8.1 如何用指针表示数组

指针表示数组的基地址,数组名就是基地址,如想索引数组中的后面的数字,则进行ptr1++。

注意:ptr1++并不是将地址值简单+1处理,而是按照指向的数组的数据类型来递增的,即+sizeof(int).

// 以下两句等同, myArray表示数组

int *ptr1 = &myArray[0];

int *ptr2 = myArray;

9.结构

 结构是一种由程序员定义的,由其他变量类型组合而成的数据类型,定义格式如下

// 结构的基本语法

struct name
{
    type varName1;
    type varName2;
}; //注意不要忘了分号

 当需要处理一些具有多种属性的数据时,结构往往是最好的选择,例如定义结构如下

// 统计成员的姓名、身份证和性别

struct FishOil
{
    std::string name;
    std::string uid;
    char sex;    //F==Female,M==Male
}

 通过下面的语法创建该类型的变量,进行赋值操作

// 通过结构创建变量
FishOil Jiayu; //创建一个FileOil结构类型Jiayu
Jiayu.name = "小甲鱼";
Jiayu.uid = "123";
Jiayu.sex = 'M';

//若创建结构类型变量的时候已经知道各个成员相关的值,可以声明新变量的同时进行赋值
FishOil Jiayu = {"小甲鱼", "123", 'M'};

10.结构与指针

 指针也可以指向结构,但索引成员变量时,不同于数组的ptr1++.

FishOil *pJiayu = &Jiayu; //创建指向结构变量Jiayu的指针
//因为指针的类型必须与指向的地址的变量类型一致,所以pJiayu指针的类型也是FishOil

访问结构变量的两种方法:

//方法一:通过指针解引用访问变量值
(*pJiayu).name = "黑夜"; //当成结构变量的时候用.
(*pJiayu).uid = "234";
(*pJiayu).sex = 'F';

//方法二:通过箭头 ->访问
pJiayu -> name = "黑夜"; //当成指针的时候用->
pJiayu -> uid = "234";
pJiayu -> sex = 'F';

std::cout << pJiayu -> name;
std::cout << pJiayu -> uid;
std::cout << pJiayu -> sex;

11.传值、传址和传引用

11.1 函数传值的局限性:无法对结果进行更新

//函数传值的局限性:无法对结果进行更改
//changeAge函数中更新的结果,不会反映到主函数的age中
//changeAge函数中的age和主函数中的age不是同一个

#include<iostream>

void changeAge(int age, int newAge);

int main()
{
    int age = 24;
    std::cout << "My age is " << age << "\n";

    changeAge(age, age+1);
    std::cout << "Now my age is " << age << "\n";

    return 0;
}

void changeAge(int age,int newAge)
{
    age = newAge;
    std::cout << "In this, my age is " << age << "\n";
}

11.2解决办法

//方法一:传址
//函数传址的优势:可以对结果进行修正

#include<iostream>

void changeAge(int *age, int newAge); //用指针代替变量名

int main()
{
    int age = 24;
    std::cout << "My age is " << age << "\n";

    changeAge(&age, age+1); //地址作为函数的输入
    std::cout << "Now my age is " << age << "\n";

    return 0;
}

void changeAge(int *age,int newAge)
{
    *age = newAge;
    std::cout << "In this, my age is " << *age << "\n";
}

//方法二:传引用

12.对象

12.1 对象和结构类似,区别是对象可以包含变量函数,而结构只能有变量。

//类的定义
//函数的使用两要素:声明+定义,在类中声明,类后定义

#include<iostream>

class Car
{
public:
    std::string color; //声明类的属性,参数
    std::string engine;
    float gas_tank;
    unsigned int wheel;

    void fill_tank(float liter); //在类中声明函数
}; //不要丢了分号

//函数(方法)的定义通常在类声明的后面
void Car::fill_tank(float liter) //::作用域解析符,fill_tank是属于Car类里面的
{
    gas_tank += liter;
}

int main()
{
    Car car1; //创建一个类的对象
    car1.color = white;
    car1.fill_tank(12);
    return 0;
}

 13.构造器和析构器

         13.1 构造器

        构造器是类中的一种特殊的方法,构造器的名字必须和所在类的名字一样,系统在创建某个类的实例时会自动调用该类的构造器,构造器不会返回任何值

        构造器的作用就是对创建的对象进行初始化,简化代码步骤,无须对创建的对象进行重复赋值。

        创建构造器,需要先把它的声明添加到类里:

// 类中声明构造器
class Car{
    Car(void); //声明构造器
};

// 类定义之后定义构造器本身 
Car::Car(void) //定义构造器
{
    color = "White";
    engine = "V8";
    wheel = 4;
    gas_tank = FULL_GAS;
}

// 构造器的使用
int main()
{
    Car car1; //创建对象时会自动调用构造器进行初始化
    return 0;
}

        构造对象数组

//构造对象数组
Car mycar[10];

//调用格式
mycar[1].running;

        副本构造器:用于对象赋值(包含指针)

Myclass(const Myclass &rhs)

        13.2 析构器

        构造器用来完成事先的初始化和准备工作(申请分配内存),析构器用来完成事后的清理工作(清理内存)。

        析构器和构造器有着一样的名字,只不过在前面多了一个波浪号“~”前缀。

class Car
{
    Car(void);
    ~Car(); //析构器,前有波浪号,不带任何参数
};

14.类的继承

        对现有的代码进行进一步的扩展,无须重新写基类中的属性和函数。

//继承的定义格式
class SubClass:public SuperClass{...} //注意时单冒号
//举例
class Pig:public Animal{...}

        实例:

#include<iostream>
#include<string>

class Animal   //定义基类
{
public:
    std::string mouth; 
    
    void eat(); //声明基类方法
    void sleep();
    void drool();
};

class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
    void climb();
};

class Turtle : public Animal
{
public:
    void swim();
};

void Animal::eat()  //定义基类方法
{
    std::cout << "I'm eatting!" << std::endl;
}

void Animal::sleep()
{
    std::cout << "I'm sleeping!" << std::endl;
}

void Animal::drool()
{
    std::cout << "I'm drooling!" << std::endl;
}

void Pig::climb() //定义子类方法
{
    std::cout << "I'm climbing!" << std::endl;
}

void Turtle::swim()
{
    std::cout << "I'm swimming!" << std::endl;
}

int main()
{
    Turtle turtle;
    Pig pig;
    turtle.eat();   //子类对象可以调用基类的方法
    turtle.swim();  //子类可以调用自己的方法
    pig.eat();
    pig.climb();

    return 0;
}

15.继承中的构造器和析构器

         子类创建对象时,先调用基类的构造器,然后再调用子类的构造器。基类必须在子类之前初始化

        构造器中包含参数的情况

//子类的构造器无任何实现
Animal::Animal(std::string theName){
    name = theName;
}
Pig::Pig(std::string theName):Animal(theName){

}

/*当调用Pig pig("小猪");将把字符串"小猪"传递给pig()和Animal(),
赋值的实际动作将发生在Animal()方法里,因为子类构造器无实际动作。
*/

        基类的析构器在子类的最后一条语句执行完毕后才被调用。主函数执行完毕后,执行子类析构器,再执行基类析构器。

16.访问控制

         访问控制是用来保护类里的方法和属性的手段。

public //任何代码可访问
protected //类本身和子类中可调用
private //类本身可访问,子类不行

//注意:主函数中通过类创建对象,再通过对象访问,属于类外访问,
//也就是说,即使是类本身创建的对象,也无法访问其protected,private

17.覆盖和重载

       17.1 覆盖

        覆盖是类里重新声明该方法,要求方法名,输入参数一致,仅内部的实现方法不一样。

//子类方法的覆盖
class Animal   //定义基类
{
public:
    std::string mouth; 
    
    void eat(); //声明基类方法
    void sleep();
    void drool();
};

class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
    void climb();
    void eat(); //将要覆盖的基类方法在子类中重新进行声明
};

//在子类的该方法中进行重新覆盖
void Pig::eat() //定义子类属性方法
{
    Animal::eat(); //继承的原基类的方法
    std::cout << "I'm eatting fish!" << std::endl; //子类中覆盖的方法
}

        17.2 重载

        重载可以定义多个同名的方法函数,但要求方法的输入参数不同

        详细内容见 5.函数的重载。

        注意:区分重载和覆盖!!

        注意:继承之后的方法函数不能进行重载!!

18.友元关系 

        解决的问题:一个完全无关的类由于某些特殊原因需要访问到某个protected成员,甚至是某个private成员的问题。

        友元关系是类之间的一个特殊关系,这种关系不仅允许友元类访问对方的public方法和属性,还允许友元访问对方的protected和private方法和属性。

        声明友元关系只需要在类声明的任意地方(可以在public、protected或private段落)加一条friend class **即可。

//友元实例

#include<iostream>
#include<string>

class Lovers
{
public:
    Lovers(std::string theName); //声明构造函数
    void kiss(Lovers *lover); //指针指向的是Lovers的对象,所以指针类型需为Lovers
    void ask(Lovers *lover, std::string something);

protected:
    std::string name;

    friend class Others; //声明友元
};

class Boyfriend : public Lovers
{
public:
    Boyfriend(std::string theName);
};

class Girlfriend : public Lovers
{
public:
    Girlfriend(std::string theName);
};

class Others
{
public:
    Others(std::string theName);
    void kiss(Lovers *lover);

protected:
    std::string name;
};

Lovers::Lovers(std::string theName) //定义构造函数
{
    name = theName;
}

void Lovers::kiss(Lovers *lover)
{
    std::cout << name << "亲亲我家的" << lover ->name << std::endl;
}

void Lovers::ask(Lovers *lover, std::string something)
{
    std::cout << "宝贝" << lover ->name << "帮我" << something << std::endl;
}

Boyfriend::Boyfriend(std::string theName) :Lovers(theName) //定义子类的构造函数
{

}

Girlfriend::Girlfriend(std::string theName) :Lovers(theName) //定义子类的构造函数
{

}

Others::Others(std::string theName) 
{
    name = theName;
}

void Others::kiss(Lovers *lover)
{
    std::cout << name << "亲一下" << lover ->name <<std::endl;
}

int main()
{
    Boyfriend boyfriend("A君");
    Girlfriend girlfriend("B妞");
    Others others("路人甲");

    girlfriend.kiss(&boyfriend);
    girlfriend.ask(&boyfriend, "洗衣服啦");

    std::cout << "\n铛铛铛铛!路人甲登场。。。。。\n";
    others.kiss(&girlfriend);

    return 0;
}

19.静态属性和静态方法

         非必要不全局变量!!因为程序中的任何地方都可以修改这个全局变量,不容易查找漏洞。

        当仅需要在创建或删除对象的时候访问计数器,则可以使用静态属性或静态函数。

        静态属性和静态方法属于该类可以在没有创建任何对象的情况下调用有关方法。     

        注意:无法在静态方法里访问非静态成员!!因为静态方法由全体对象共享  

        静态化可以让有关的数据仍在该类的所有对象间共享

        定义方法:在声明前添加static关键字。

//静态属性、静态方法实例

#include<iostream>
#include<string>

class Pet
{
private:
    static int count;   //声明静态属性,仅类内部通过构造函数可修改该值

protected:
    std::string name;

public:
    Pet(std::string theName);
    ~Pet();

    static int getCount();  //声明静态方法,提供外部读取接口
};

class Dog : public Pet
{
public:
    Dog(std::string theName);
};

class Cat : public Pet
{
public:
    Cat(std::string theName);
};

//在类声明的外部进行声明
int Pet::count = 0; //作用1:开辟存储空间;作用2:初始化为0

Pet::Pet(std::string theName)
{
    name = theName;
    count++;

    std::cout << "一只宠物出生了,名字叫做: " << name << "\n";
}

Pet::~Pet()
{
    count--;

    std::cout << name << "挂掉了\n";
}

int Pet::getCount()
{
    return count;
}

Dog::Dog(std::string theName) : Pet(theName)
{

}

Cat::Cat(std::string theName) : Pet(theName)
{
    
}

int main()
{
    Dog dog("Tom");
    Cat cat("Jerry");

    //静态方法使用ClassName::methodName()调用,不要用objectName.methodName()调用!!
    std::cout << "\n已经诞生了" << Pet::getCount() << "只宠物!\n\n";

    {   //花括号的用处是限定作用范围,当花括号运行完毕时,执行析构函数
        Dog dog_2("Tom_2");
        Cat cat_2("Jerry_2");

        std::cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!\n\n";
    }

    std::cout << "\n现在还剩下 " << Pet::getCount() << "只宠物!\n\n";

    return 0;
}

20.虚方法

         1.引出目的:为了解决让C++调用运行时定义的类型而不是编译时调用的类型,即按照程序设计的预期工作。

//虚方法引入的必要性

#include<iostream>
#include<string>

class Animal   //定义基类 
{
public:   
    void eat(); //声明基类方法
    // virtual void eat(); 
};

class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
    void eat(); //方法继承覆盖
};

void Animal::eat()  //定义基类方法
{
    std::cout << "I'm eatting!" << std::endl;
}

void Pig::eat() //定义子类属性方法
{
    Animal::eat();
    std::cout << "I'm eatting pig!" << std::endl;
}

int main()
{
    /* 
    此处,C++编译时会根据指针类型选为Animal的成员方法,而我们想要的是
    C++会根据后面的new Pig,生成Pig类型的指针
    */
    Animal *pig = new Pig;
    pig ->eat();

    return 0;
}

/*

//虚方法的作用实例

virtual void eat(); //声明基类方法,仅需前加virtual

*/

运行结果:

I'm eatting!
I'm eatting!

        2.解决办法:

        仅虚在重覆盖的基类方法声明前加上virtual

virtual void eat(); 

运行结果:

I'm eatting!
I'm eatting fish!

        注意:虚方法是继承的,在基类中声明为虚方法,在子类中就不能再声明为非虚方法了!!

基类中的析构函数也是虚函数,子类会自动继承基类的虚函数。

 21.抽象方法

         在声明宠物玩的方法时候,不存在什么都能玩的情况,仅仅是为了继承使用,所以可以设置为抽象方法。抽象方法的使用仅需在声明虚方法的基础上,在原型末尾加上“=0”(编译器无须寻找该方法的实现,仅在继承的时候才考虑它的实现)

class Animal   //定义基类 
{
public:   
    virtual void eat() = 0; //抽象方法的声明 
};

//下面关于基类该抽象方法的实现可以删去,没有必要了
// void Animal::eat()  //定义基类方法
// {
//     std::cout << "I'm eatting!" << std::endl;
// }

        疑问:既然基类中仅设置一个空声明不用写实现,那直接在子类中以扩展的方式声明实现不就好了,为什么还要多次一举??

22.多态性

         一个接口,多种方法!

        1.多态实现绑定的两种方法:

        1)编译时实现:通过重载 --运行速度快

        2)运行时实现:通过虚函数 --高度灵活和抽象

23.运算符重载

函数类型 operator 运算符名称(形参列表)
{
    对运算符的重载处理
}

重载中,函数类型最好是用它函数的类型,即它的类。

//对运算符“+”进行重载实例

#include<iostream>

class Complex
{
public:
    Complex(); //无参构造函数声明
    Complex(double r, double i);    //有参构造函数声明
    Complex operator+(Complex &d); //重载函数类型写成函数的类型,也即函数所在的类Complex
    void print();

private:
    double real;
    double imag;
};

Complex::Complex()  //定义无参构造函数
{
    real = 0;
    imag = 0;
}

Complex::Complex(double r, double i)    //定义有参构造函数
{
    real = r;
    imag = i;
}

//函数类型 函数作用域::operator 运算符(形参)
Complex Complex :: operator+(Complex &d)    //定义运算符重载函数
{
    Complex c; //创建实例对象

    c.real = real + d.real;
    c.imag = imag + d.imag;

    return c;
}

void Complex::print()
{
    std::cout << "(" << real << "," << imag << "i)\n";
}

int main()
{
    Complex c1(3,4), c2(5,-10), c3; //创建三个对象,通过构造函数初始化

    c3 = c1 + c2;
    std::cout << "c1 = ";
    c1.print();
    std::cout << "c2 = ";
    c3.print();
    std::cout << "c1 + c2 = ";
    c3.print();

    return 0;
}

1.以下五个运算符不允许重载,此外均可

.(成员访问运算符)
.*(成员指针访问运算符)
::(域运算符)
sizeof(尺寸运算符)
?:(条件运算符)

2.C++不允许自定义新的运算符,只能在现有的基础上进行重载。

3.运算符重载规则

        运算符重载不能改变运算符运算对象(操作数)个数。

        不能改变运算符的优先级别。

        不能改变运算符的结合性。

24.操作符重载

1.为什么要以普通函数的形式在类外进行定义?为什么不能像上面的加运算符重载一样在类里实现??以普通函数的形式为什么可以重载<<操作符??

在类中重载的操作符,作用域仅在该类中??

运算符重载operator前无&,操作符重载operator前有&?(好像是因为下面两个是以地址为介质处理数据的,所以用取地址&)

//对<<操作符进行重载
//类里声明友元函数
class A
{
public:

private:
    int x;
    int y;
    friend std::ostream& operator<<(std::ostream& os, A a);
};

//类外声明该普通函数
std::ostream& operator<<(std::ostream& os, A a);

//普通函数定义
std::ostream& operator<<(std::ostream& os, A a)
{
    os << a.x << "/" << a.y;
    return os;
}

//对 = 操作符进行重载
Myclass &operator = (const Myclass &rhs);

25.assert函数

//引用的头文件
#include<cassert>

//A为真,啥也不做;A为假,做某事.
assert(A);

26.动态内存

        1.动态内存由没有名字、只有地址的内存块构成,是在程序运行期间动态分配的。

        2.使用new语句动态分配内存,new语句将返回新分配地址块的起始地址

        3.用完内存块之后,应及时用delete语句删除内存块;同时将与之关联的指针设置为NULL

        4.new返回的地址块可能有先前的东西,所以最好是先覆盖再访问,或者直接在类中的构造器进行初始化

        5.动态内存块没有作用域,但是用来保存其地址的指针变量是受作用域影响的。

Company *company = new Company("Apple");    //创建动态内存
company -> printInfo();

delete company;     //删除动态内存
company = NULL;     //删除动态内存对应的地址

//company对应的地址已经被删除,所以下面可以再次使用该指针
company = new TechCompany("Apple", "Iphone");
company -> printInfo();

delete company;
company = NULL;

27.动态数组

 1.新建一个动态数组

int *x = new int[10];
x[1] = 45;
x[2] = 8;

//或者变量保存数组元素个数
int count = 10;
int *x = new int[count];

 2.删除一个动态数组

delete []x;

 28.函数模板

template <class T> //<class T>表示T在接下来的函数里代表一种不确定的数据类型
                   //class不代表类,仅仅是一种表示
void foo(T param)
{
    do something
}

 实例:

//函数模板--交换

#include<iostream>
#include<string>

//template <typename T>也可
template <class T>
void swap(T &a, T &b)
{
    T tmp = a; 
    a = b;
    b = tmp;
}

int main()
{
    int i1 = 100;
    int i2 = 200;
    std::cout << "交换前,i1 = " << i1 << ", i2 = " << i2 << "\n";
    swap(i1, i2);
    std::cout << "交换后,i1 = " << i1 << ", i2 = " << i2 << "\n";
    
    std::string s1 = "小甲鱼";
    std::string s2 = "加菲猫";
    std::cout << "交换前,s1 = " << s1 << ", s2 = " << s2 << "\n";
    swap(s1, s2);
    std::cout << "交换后,s1 = " << s1 << ", s2 = " << s2 << "\n";


    return 0;
}

29.类模板

 #include<iostream>
 #include<string>

 template <class T>
 class Stack
 {
public:
    Stack(unsigned int size = 100);
    ~Stack();
    void push(T value);
    T pop();

private:
    unsigned int size;
    unsigned int sp;
    T *data;    //栈指针
 };

template <class T>
Stack<T>::Stack(unsigned int size)
{
    this -> size = size;
    data = new T[size];
    sp = 0;
}

template <class T>
Stack<T>::~Stack()
{
    delete []data;
}

template <class T>
void Stack<T>::push(T value)
{
    data[sp++] = value;
}

template <class T>
T Stack<T>::pop()
{
    return data[--sp];
}

int main()
{
    Stack<int> intStack(100);

    intStack.push(1);
    intStack.push(2);
    intStack.push(3);

    std::cout << intStack.pop() << "\n";
    std::cout << intStack.pop() << "\n";
    std::cout << intStack.pop() << "\n";
    
    return 0;
}

 30.容器vector

vector的容量是无限的,根据存入的数据动态扩容

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

int main()
{
    std::vector<std::string> names;

    names.push_back("小甲鱼");
    names.push_back("小鱿鱼");

    for(int i=0; i<names.size(); i++)
    {
        std::cout << names[i] << "\n";
    }
    return 0;
}

31.迭代器

 迭代器是个智能指针,具有遍历复杂数据结构的能力。

作用是用来遍历容器,而且是通用的遍历容器元素的方式,无论容器是基于什么数据结构实现的,尽管不同的数据结构,遍历元素的方式不一样,但是用迭代器遍历不同容器的代码是完全一样的

//解决的问题:适用于所有容器,各容器方式不同
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::vector<std::string> names;

    names.push_back("小甲鱼");
    names.push_back("小鱿鱼");

    std::vector<std::string>::iterator iter = names.begin();

    while(iter != names.end())
    {
        std::cout << *iter << "\n";
        ++iter;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值