C++第一天:C++面向对象高级开发上

使用Clion编译器,C++11

头文件与类的声明

引用头文件:如果是系统的头文件用<>,如果是自己编写的头文件用""。
延伸文件名不一定是.h或者.cpp,可能是.hpp或其他或无延伸名
比如下面的程序,如果写成iostream.h反而报错

#include <iostream>
#include "my.h"
int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

防卫式声明guard:
complex.h

#ifndef __COMPLEX__
#define __COMPLEX__

...

#endif

类的声明:

complex.h:有些函数在此直接定义,有些函数只是声明

//
// Created by l30035748 on 2022/8/4.
//

#ifndef COMPLEX_COMPLEX_H
#define COMPLEX_COMPLEX_H

class complex{
public :
    complex (double r = 0,double i = 0)
    : re(r),im(i)
    {}
    complex& operator += (const complex&);
    double real()const {return re;}
    double  imag()const {return im;}
private:
    double re, im;

    friend complex& __doapl(complex *,const complex&);

};

#endif //COMPLEX_COMPLEX_H

inline内联函数

函数在类的本体中定义,自动成为inline函数,效率很高
但是函数太复杂就没办法inline,是否是inline由编译器决定
关键字 inline

访问级别

public、private、protected

构造函数

创建对象时系统自动调用

complex (double r = 0,double i = 0) // r和i的默认值是0,也就是如果创建时候不传参,默认就是0,0
    : re(r),im(i)  //初始化列表,只有构造函数才有,推荐构造函数使用这种方式
    {}

在括号里写也完全ok,但是效率会低,原因如下:构造会有两阶段,第一阶段是初始化,第二阶段是赋值,如果在括号里写,就相当于放弃第一阶段的初始化,而等到第二阶段才来设值。

函数重载

构造函数可以有overloading重载

参数传递和返回值

常量成员函数

const member functions

doublie real() const{return re;}

const complex c1(1,2); 
cout << c1.real();  //如果上面方法不是const的,现在complex又是const的,则该方法调用失败,因为方法可修改,但是用户认为c1不可修改,两相矛盾

加了const则代表不能修改变量

参数传递

值传递和引用传递(是否const)

complex (double r = 0,double i = 0)
    : re(r),im(i)
    {}
complex& operator += (const complex&); // complex& 代表引用,加const代表常量引用

尽量不要传值,因为值可能很大,传引用(底部就是指针,四个字节),不希望别人修改值就加const。

返回值传递

返回值或者返回引用(是否const)

complex& operator += (const complex&); //返回引用
double real()const {return re;} //返回值

friend 友元

private:
    double re, im;

    friend complex& __doapl(complex *,const complex&);
inline complex& __doapl(complex* ths,const complex& r) //自由获取friend的private成员
{
	ths->re += r.re;
	ths->im += r.im;
	return *this;
}

正常来说private里面的数据是不允许被类外访问,但是加了friend之后,friend函数就可以直接拿private里面的数据
friend会破坏封装,也可以利用函数进行访问,但是效率会低一点。

相同class 的各个objects 互为friend(友元)

complex类中方法可以看出来

int func(const complex& param)
    {return param.re + param.im;}

什么时候可以引用传递,什么时候可以返回引用

inline complex& __doapl(complex* ths,const complex& r) 
{
	ths->re += r.re;
	ths->im += r.im;
	return *this;
}
//下面这种不能返回引用,因为temp是临时值,如果是返回引用,函数结束后temp会被回收,引用指向的地址就有问题
inline complex& __doapl(complex* ths,const complex& r) 
{
	complex temp(0,0);
	temp.re = ths->re + r.re;
	temp.im = ths->im + r.im;
	return temp;
}

操作符重载和临时对象

inline complex& __doapl(complex* ths,const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}
inline complex& complex::operator+=(const complex& r) {
    return __doapl(this,r);
}

返回引用语法分析

传递者不需要知道接收者是以引用形式接收
也就是返回可以是值,接收值的人声明是引用类型也不会有问题

inline complex& __doapl(complex* ths,const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths; //传递者返回值
}

操作符重载2,非成员函数

上面的+=操作符重载中,函数定义写了complex::代表是complex的成员函数
下面写三个非成员函数的操作符重载

inline complex operator+ (const complex& x, const complex& y)
{
	return complex( real(x) + real(y),imag(x) + imag(y) );
}
inline complex operator+ (const complex& x, double y)
{
	return complex( real(x) + y,imag(x));
}
inline complex operator+ (double x, const complex& y)
{
	return complex( x + real(y),imag(y) ); //临时对象,格式typename();
}

与成员函数的区别是成员函数有this指针,全局函数没有,必须两个参数都标明
上述函数绝对不能返回引用,因为他们返回的是一个本地对象,放在函数栈中,函数结束栈就销毁这个对象,所以不能传引用

正负号函数重载如下

inline complex operator + (const complex& x)
{
	return x;
}
inline complex operator - (const complex& x)
{
	return complex(-real(x), -imag(x));
}

三大函数Big Three:拷贝构造、拷贝赋值、析构

如果不写拷贝构造,系统会默认创建一套拷贝构造的函数,但是对于指针来说,会导致两个变量指向同一个空间,所以我们必须自己实现拷贝构造和拷贝赋值。(浅拷贝和深拷贝)

string.h

//
// Created by l30035748 on 2022/8/4.
//

#ifndef __MYSTRING__
#define __MYSTRING__

#include <cstring>
#include <iostream>
using namespace std;
class String
{
public:
    String(const char* cstr = 0);
    String(const String & str); // 拷贝构造
    String& operator= (const String& str); // 拷贝赋值
    ~String(); // 析构函数,当对象死亡时,会调用该函数
    char* get_c_str() const {return m_data; }
private:
    char * m_data;
};
//普通构造
inline String::String(const char* cstr)
{
    if(cstr){
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data,cstr);
    }
    else{ //未指定初始值,此时cstr = 0
        m_data = new char [1];
        *m_data = '\0';
    }
}
//拷贝构造
inline String::String(const String & str){
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data,str.m_data);
}
//拷贝赋值
inline String& String::operator= (const String& str)
{
    if(this == &str)
        return *this;
    delete[] m_data;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data,str.m_data);
    return *this;
}
//析构函数
inline String::~String() {
    delete[] m_data;
}
//<<重载,不能写成成员函数,这样会导致str << out;
ostream& operator<<(ostream& os, const String& str)
{
    os << str.get_c_str();
    return os;
}
#endif //__MYSTRING__

堆、栈与内存管理

stack栈:存在于某作用域(scope)的一块内存空间。例如当你调用函数,函数本身即会形成一个stack用来放置它所接收的参数,以及返回地址。
在函数本体内声明的任何变量,其所使用的内存块都取自上述stack。

heap堆:指由操作系统提供的一块global内存空间,程序可动态分配,从其中获得若干区块。

栈对象生命在作用域运行时,当作用域结束之后,就会自动调用析构函数释放内存
static对象生命在作用域结束后依然存在,直到整个程序结束。
全局对象生命在整个程序结束后才结束。
堆对象在new之后创建,在用完记得delete,不然指针死亡而空间还在,会导致内存泄漏。

new:先分配空间(malloc),再类型转化,再调用构造

delete:先调用析构函数,再释放内存(free)

动态分配所得的内存块细节看视频

array new 一定要搭配array delete

扩展补充:类模板、函数模板以及其它

static

独一份
调用static函数的方式有两种:

  1. 通过object调用
  2. 通过class name调用

#include "iostream"
using namespace std;

class Account{
public:
    static double m_rate;
    static void set_rate(const double& x){m_rate = x;};
};
double Account::m_rate = 8.0;


int main(){
    Account::set_rate(5.0); // 2.
    Account a;
    a.set_rate(7.0); // 1.
    cout << a.m_rate << endl;
}

static 实现单例的两种方式


class A{
public:
    static A& getInstance(){return a;};
private:
    A();
    A(const A& rhs);
    static A a;
};

class A1{
public:
    static A1& getInstance();
private:
    A1();
    A1(const A1& rhs);
};
A1& A1::getInstance() {
    static A1 a1;
    return a1;
}

cout

底层就是集成自ostream

类模板

template<typename T>
class complex
{
public:
    complex(T r = 0,T i =0 ) :re(r), im(i){};
    complex& operator += (const complex&);
    T real() const {return re;}
    T imag() const {return im;}
    
private:
    T re,im;
    friend complex& __doapl(complex* , const complex&);
};

用的时候

complex<double> c1(2.5,1.5);
complex<int> c2(2,6);

函数模板

template<class T>
inline const T& min(const T& a,const T& b)
{
    return b < a ? b : a;
}

用的时候不必指明类型,编译器会对函数模板进行引数推导(argument deduction)

int a = 2,b = 3;
a = min(a,b);

namespace

//创建自己的命名空间
namespace my
{

}
//使用方法一:直接使用整个命名空间
#include "iostream"
using namespace std;
int main()
{
    int a;
    cin >> a ;
    cout << "..." << endl;
}
//使用方法二:使用声明
#include "iostream"
using std::cout;
int main()
{
    int a;
    std::cin >> a ;
    cout << "..."  << std::endl;
}

//使用方法三:不使用命名空间
#include "iostream"
int main()
{
    int a;
    std::cin >> a ;
    std::cout << "..."  << std::endl;
}

组合与继承(面向对象)

Composition复合,表示has-a

同步创建

class Container{
protected:
	Component com;
}

构造由内而外
Container的构造函数首先调用Component的default构造函数,然后才执行自己。
编译器编译时:Container::Container(…) : Component(){…};
析构由外而内
Container的析构函数首先执行自己,然后才调用Component的析构函数
Container::~Container(…){… ~Component() };

Delegation委托,Composition by reference(引用组成)

不同步创建,内部成员是一个指针
Handle / Body

class String{
...
private:
	StringRep* rep; // pimpl
}
//。。。
class StringRep{
...
}

Inheritance继承,表示is-a

三种继承方式:public protected private

class Father
{
	...
}

class Child : public Father
{
	...
}

子类的类中有父类的成分

构造由内而外
子类先调用父类的构造函数,再执行自己的构造函数

析构由外而内
子类先调用自己的析构函数,然后才调用父类的析构函数
父类的析构函数必须是virtual的,否则会出现undefined behavior

虚函数与多态

虚函数

当我们使用继承的时候,我们需要搭配虚函数来使用,效果更好。

non-virtual函数:不希望子类重新定义函数(override,覆盖)
virtual函数:希望子类重新定义函数,而且现在这函数已经有默认定义
pure virtual函数:希望子类一定要重定义函数,现在对这函数没有默认定义

#include <iostream>

class Shape
{
public:
    virtual void draw()const = 0; //纯虚函数 pure virtual
    virtual void error(const std::string& msg); //虚函数
    int objectID()const; //非虚函数
};

void Shape::error(const std::string &msg) {
    std::cout << "默认错误" << std::endl;
}

class Rectangle : public Shape
{
public:
    void error(const std::string &msg) override;
    void draw() const override;
};

void Rectangle::error(const std::string &msg) {
    std::cout << "子类错误" << std::endl;
}
inline void Rectangle::draw() const { //
    std::cout << "aa只能子类实现" << std::endl;
}

虚函数还需多看一下具体语法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来为您生成一个C++面向对象的PPT。 第一页:标题页 标题:C++面向对象 第二页:目录页 1. C++简介 2. 面向对象编程基础 3. 类和对象 4. 继承与多态 5. 抽象类和接口 6. 组合与聚合 7. 异常处理 8. STL库简介 第三页:C++简介 - C++是一种高级编程语言,支持面向对象编程和泛型编程,以及低级的系统编程。 - C++语言由Bjarne Stroustrup在1983年发明,它是C语言的扩展版。 第四页:面向对象编程基础 - 面向对象编程的核心思想是把数据和方法封装在一个对象中。 - 面向对象编程有三个基本特征:封装、继承和多态。 第五页:类和对象 - 类是一种自定义数据类型,它包含属性和方法。 - 对象是类的实例,可以使用类中的方法来操作对象的属性。 第六页:继承与多态 - 继承是指派生类继承基类的属性和方法。 - 多态是指同一种操作可以作用于不同的对象,产生不同的结果。 第七页:抽象类和接口 - 抽象类是一种特殊的类,不能被实例化,只能被继承。 - 接口是一种特殊的抽象类,只包含抽象方法,不能包含属性。 第八页:组合与聚合 - 组合是指一个类包含另一个类的对象。 - 聚合是指一个类包含另一个类的指针或引用。 第九页:异常处理 - 异常是指程序执行过程中出现的错误或异常情况。 - 异常处理是指在程序中捕获和处理异常,保证程序的稳定性和可靠性。 第十页:STL库简介 - STL库是C++标准库的一部分,包含了许多有用的容器、算法和迭代器。 - STL库的使用可以提高程序的效率和可读性。 第十一页:总结页 - C++是一种支持面向对象编程和泛型编程的高级编程语言。 - 面向对象编程有三个基本特征:封装、继承和多态。 - C++标准库中的STL库包含了许多有用的容器、算法和迭代器,可以提高程序的效率和可读性。 这就是我为您生成的C++面向对象的PPT,希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值