<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C++笔记 - 南极滑稽的博客</title>
	<atom:link href="https://blog.nanjihuaji.top/category/programming/note-of-cpp/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.nanjihuaji.top</link>
	<description>一个学术垃圾的博客</description>
	<lastBuildDate>Fri, 02 Aug 2024 04:08:57 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.nanjihuaji.top/wp-content/uploads/2024/08/cropped-Cache_45925d60c206ddd8-150x150.jpg</url>
	<title>C++笔记 - 南极滑稽的博客</title>
	<link>https://blog.nanjihuaji.top</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>类继承（2）</title>
		<link>https://blog.nanjihuaji.top/2023/12/08/%e7%b1%bb%e7%bb%a7%e6%89%bf%ef%bc%882%ef%bc%89/</link>
					<comments>https://blog.nanjihuaji.top/2023/12/08/%e7%b1%bb%e7%bb%a7%e6%89%bf%ef%bc%882%ef%bc%89/#comments</comments>
		
		<dc:creator><![CDATA[Nanjihuaji]]></dc:creator>
		<pubDate>Fri, 08 Dec 2023 07:36:45 +0000</pubDate>
				<category><![CDATA[C++笔记]]></category>
		<category><![CDATA[编程]]></category>
		<guid isPermaLink="false">http://temp.nanjihuaji.top/?p=98</guid>

					<description><![CDATA[<p>公有继承 继承方式： 父类 子类 public public protected protected priv [&#8230;]</p>
<p>The post <a href="https://blog.nanjihuaji.top/2023/12/08/%e7%b1%bb%e7%bb%a7%e6%89%bf%ef%bc%882%ef%bc%89/">类继承（2）</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">公有继承</h2>



<pre class="wp-block-code"><code>class 子类名: public 父类名{
};</code></pre>



<p>继承方式：</p>



<figure class="wp-block-table"><table><tbody><tr><td>父类</td><td>子类</td></tr><tr><td>public</td><td>public</td></tr><tr><td>protected</td><td>protected</td></tr><tr><td>private</td><td>不能读取父类的private</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">私有继承</h2>



<p>类继承的缺省继承方式是private继承。</p>



<pre class="wp-block-code"><code>class 子类名: private 父类名{
};</code></pre>



<figure class="wp-block-table"><table><tbody><tr><td>父类</td><td>子类</td></tr><tr><td>public</td><td>private</td></tr><tr><td>protected</td><td>private</td></tr><tr><td>private</td><td>不能读取父类的private</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">保护继承</h2>



<pre class="wp-block-code"><code>class 子类名: protected 父类名{
};</code></pre>



<figure class="wp-block-table"><table><tbody><tr><td>父类</td><td>子类</td></tr><tr><td>public</td><td>protected</td></tr><tr><td>protected</td><td>protected</td></tr><tr><td>private</td><td>不能读取父类的private</td></tr></tbody></table></figure>



<p>基类的protected成员不能被派生类的对象访问。</p>



<h2 class="wp-block-heading">同名覆盖</h2>



<ol class="wp-block-list">
<li>子类修改基类的成员，是在子类中声明了一个与父p;类成员同名的新成员。</li>



<li>在子类作用域内或者在类外通过子类的对象直接使用这个成员名，只能访问到子类中生命的同名新成员，这个新成员覆盖了从基类继承的同名成员，这种情况称为同名覆盖。</li>



<li>需要注意的是，同名覆盖的函数的函数名、参数需要<strong>完全一样</strong>，否则函数会被重载而不是被同名覆盖。</li>
</ol>



<h2 class="wp-block-heading">子类的构造和析构函数</h2>



<ol class="wp-block-list">
<li>基类的构造函数和析构函数不能直接被派生类所继承。</li>



<li>派生类一般需要自己定义构造和析构函数。</li>



<li>派生类的构造及析构函数通常会收到基类的影响。</li>
</ol>



<p>若基类只有无参数构造函数：</p>



<ol class="wp-block-list">
<li>在基类具有<strong>无参</strong>构造函数，派生类又没有定义构造函数的时候，系统会自动调用基类的无参构造函数来<strong>构造派生类对象中的<em>基类部分</em></strong>。</li>



<li>基类的构造函数一般为public的访问控制方式。若基类提供了一些构造函数，并且只希望由派生类使用这些函数，那么就需要在基类中将这样的特殊构造函数定义为protected。</li>
</ol>



<p>派生类没有自己的析构函数，需要自己定义析构函数，以便在派生类对象消亡之前进行必要的清理工作。</p>



<ol class="wp-block-list">
<li>派生类的析构函数只负责清理它新定义的成员，一般来说，只清理位于堆区的成员。</li>



<li>如果没有特殊指针数据成员需要清理，可以使用由系统默认提供的析构函数。</li>



<li>当派生类对象消亡时，系统调用析构函数的顺序与建立派生类对象时调用构造函数的顺序正好相反，即先调用派生类的析构函数，再调用基类的析构函数。（即构造时：基类->派生类，析构时：派生类->基类）。</li>
</ol>



<h2 class="wp-block-heading">多继承</h2>



<p>多继承即一个子类可以有多个父类，它继承了多个父类的特性。</p>



<pre class="wp-block-code"><code>class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};</code></pre>



<p>C++ 提出了虚继承，使得在派生类中只保留一份间接基类的成员。</p>



<p>在继承方式前面加上 virtual关键字就是虚继承，请看下面的例子：</p>



<pre class="wp-block-code"><code>//间接基类A
class A{
protected:
    int m_a;
};

//直接基类B
class B: virtual public A{  //虚继承
protected:
    int m_b;
};

//直接基类C
class C: virtual public A{  //虚继承
protected:
    int m_c;
};

//派生类D
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  //正确
    void setb(int b){ m_b = b; }  //正确
    void setc(int c){ m_c = c; }  //正确
    void setd(int d){ m_d = d; }  //正确
private:
    int m_d;
};

int main(){
    D d;
    return 0;
}</code></pre>



<p>这段代码使用虚继承重新实现了上图所示的菱形继承，这样在派生类 D 中就只保留了一份成员变量 m_a，直接访问就不会再有歧义了。</p>



<p>虚继承的目的是让某个类做出声明，承诺愿意共享它的基类。其中，这个被共享的基类就称为虚基类（Virtual Base Class），本例中的 A 就是一个虚基类。在这种机制下，不论虚基类在继承体系中出现了多少次，在派生类中都只包含一份虚基类的成员。</p>



<h2 class="wp-block-heading">转换与继承</h2>



<p>每个派生类对象包含一个基类部分，这意味着可以使用基类对象一样在派生类对象上执行积累的操作。这就涉及到派生类和基类的转换。该转换包括以下三种情况：</p>



<ol class="wp-block-list">
<li>派生类对象转换成基类对象（用派生类对象给基类对象赋值）</li>



<li>基类对象指针指向派生类对象。</li>



<li>用派生类对象初始化基类对象的引用。</li>
</ol>



<p></p><p>The post <a href="https://blog.nanjihuaji.top/2023/12/08/%e7%b1%bb%e7%bb%a7%e6%89%bf%ef%bc%882%ef%bc%89/">类继承（2）</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://blog.nanjihuaji.top/2023/12/08/%e7%b1%bb%e7%bb%a7%e6%89%bf%ef%bc%882%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>类继承</title>
		<link>https://blog.nanjihuaji.top/2023/12/02/%e7%b1%bb%e7%bb%a7%e6%89%bf/</link>
					<comments>https://blog.nanjihuaji.top/2023/12/02/%e7%b1%bb%e7%bb%a7%e6%89%bf/#respond</comments>
		
		<dc:creator><![CDATA[Nanjihuaji]]></dc:creator>
		<pubDate>Sat, 02 Dec 2023 08:06:27 +0000</pubDate>
				<category><![CDATA[C++笔记]]></category>
		<category><![CDATA[编程]]></category>
		<guid isPermaLink="false">http://temp.nanjihuaji.top/?p=96</guid>

					<description><![CDATA[<p>类继承是在现有类的基础上创造新类的机制。 称现有的类为基类（父类），建立的类为派生类（子类）。 如果两个类的实 [&#8230;]</p>
<p>The post <a href="https://blog.nanjihuaji.top/2023/12/02/%e7%b1%bb%e7%bb%a7%e6%89%bf/">类继承</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>类继承是在现有类的基础上创造新类的机制。</p>



<p>称现有的类为基类（父类），建立的类为派生类（子类）。</p>



<p>如果两个类的实现有某些显著的共同点，则将这些共同点做成一个基类。</p>



<p>若派生类有一个直接基类，记作单继承；反之记作多继承。</p>



<p>继承方式：public, private, protected.</p>



<p>不同继承方式的影响方式主要是：</p>



<ol class="wp-block-list">
<li>派生类<strong>成员</strong>对基类<strong>成员</strong>的访问控制</li>



<li>派生类<strong>对象</strong>对基类<strong>成员</strong>的访问控制</li>
</ol>



<p>下面举一个例子：</p>



<pre class="wp-block-code"><code>//下面我们以画图形为例，下列文本在Tshape.h中
class TShape{ //基类
    private:
        int color;
        int x;
        int y;
    public:
        TShape(); //构造函数
        int getx();
        int gety();
        void setx(int nx);
        void sety(int ny);
        void draw();
};
//下列的代码在Tshape.cpp中
#include "TShape.h"
#include <iostream>
TShape::TShape(){ //构造函数，默认初始化图像的中心点为(10, 10)
    x = 10;
    y = 10;
}
std::void TShape::Draw(){
    std::cout<<"画了吗？如画"<< std::endl;
}</code></pre>



<p>在上面，我们成功写出了一个最基础的基类shape，随后，我们可以在shape下面继续构造circle, triangle等等子类。子类继承shape的color，x, y等成员，但同时具有自己独有的成员和方法。</p>



<pre class="wp-block-code"><code>//TEllipse.h（T椭圆.h）
#include "TShape.h"
class TEllipse<strong>: </strong>public Tshape{  //单冒号表示继承关系，双冒号表示作用域运算符
//继承后，没有写的父类会全部被复制过来
    public:
        void Draw(); //父类里面已经有了，这个Draw会被替换掉.
        void seta(int na);
        void setb(int nb);
    private:
        int a;
        int b;
}
//TEllipse.cpp
#include "TEllipse.h"
#include <iostream>
void TEllipse::Draw(){
    std::cout<<"这是椭圆，但画了吗？如画"<<std::endl;
}
void seta(int a){
    a = na;
}
//Main文件
#include "TEllipse01.h"
#include <iostream>
using namespace std;
int main(){
    TEllipse myEllipse;
    cout << "x = " << myEllipse.getx() << endl;
    cout << "y = " << myEllipse.gety() << endl; // getx和gety被继承。
    TEllipse::Draw();
    return 0;
}</code></pre>



<p></p><p>The post <a href="https://blog.nanjihuaji.top/2023/12/02/%e7%b1%bb%e7%bb%a7%e6%89%bf/">类继承</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://blog.nanjihuaji.top/2023/12/02/%e7%b1%bb%e7%bb%a7%e6%89%bf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>C++中的面向对象</title>
		<link>https://blog.nanjihuaji.top/2023/12/01/c%e4%b8%ad%e7%9a%84%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/</link>
					<comments>https://blog.nanjihuaji.top/2023/12/01/c%e4%b8%ad%e7%9a%84%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/#respond</comments>
		
		<dc:creator><![CDATA[Nanjihuaji]]></dc:creator>
		<pubDate>Fri, 01 Dec 2023 06:50:39 +0000</pubDate>
				<category><![CDATA[C++笔记]]></category>
		<category><![CDATA[编程]]></category>
		<guid isPermaLink="false">http://temp.nanjihuaji.top/?p=83</guid>

					<description><![CDATA[<p>俗话说，面向对象是C++区别于C的基础和核心。本文将简要介绍C++的类与对象。 访问控制属性 public p [&#8230;]</p>
<p>The post <a href="https://blog.nanjihuaji.top/2023/12/01/c%e4%b8%ad%e7%9a%84%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/">C++中的面向对象</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>俗话说，面向对象是C++区别于C的基础和核心。本文将简要介绍C++的类与对象。</p>



<h2 class="wp-block-heading">访问控制属性</h2>



<figure class="wp-block-table"><table><tbody><tr><td>public</td><td>private</td><td>protected</td></tr><tr><td>可以类外访问</td><td>只能由类成员和友元访问</td><td>可以被派生类对象访问</td></tr><tr><td>一部分函数可以是public的</td><td>数据尽量定义成private的</td><td></td></tr></tbody></table></figure>



<p>类的实现</p>



<ol class="wp-block-list">
<li>类的成员函数描述的是类的行为或操作</li>



<li>函数的原型声明要在类的主体中，而函数的实现一般写在类声明之外</li>



<li>定义类的成员函数：</li>
</ol>



<pre class="wp-block-code"><code>返回值类型 类名::成员函数名(参数表){
    函数体
}</code></pre>



<p>    4. 成员函数可以访问本类的任意成员。这表明成员函数可以直接调用类里面已经定义的所有成员，无论是public, private还是protected.</p>



<p>面向对象一般写三个文件，一个是定义类成员函数的头文件（.h），一个是实现函数的.cpp文件，一个是主程序文件。下面举个例子。</p>



<p>这是clock.h中的内容：<br></p>



<pre class="wp-block-code language-c"><code>//clock.h
class Clock{
    public:
        void setTime(int NHour, int NMinute, int NSecond);//参数可以带默认值，但注意要在右边
        void showTime();
    private:
        int Hour;
        int Minute;
        int Second;
};
//clock.cpp
#include "Clock.h"
void Clock::setTime(int NHour, int NMinute, int Nsecond){//setTime()函数
    Hour = NHour;
    Minute = NMinute;
    Second  = NSecond;
void Clock::showTime(){
    cout << Hour << ":" << Minute << ":" << Second << endl;
}</code></pre>



<h2 class="wp-block-heading">对象</h2>



<p>定义对象和定义一般变量并无不同，为：<类名> <对象名>;例如：</p>



<pre class="wp-block-code"><code>#include "Clock.h"
#include <iostream>
using namespace std;
int main(){
    Clock myclock;
    myclock.setTime(12, 5, 0);
    myclock.showTime();
}</code></pre>



<p>要注意的是，类中的变量一定是不能有初始值的。因为类是一个抽象的概念，不占用空间。</p>



<p>实例化之后，这个实例的变量会被分配空间，但是函数是不占用空间的。</p>



<h2 class="wp-block-heading">类的作用域</h2>



<p>类作用域是指类定义和相应的成员函数定义的范围，通俗地称为类的内部。</p>



<p>c++认为一个类的全部成员都是一个整体的相关部分。</p>



<p>一个类的所有成员位于这个类的作用域内，在该范围内，一个类的成员函数对本类的其它成员拥有无限制的访问权。</p>



<h2 class="wp-block-heading">类的可见性</h2>



<ol class="wp-block-list">
<li>类名实际上是个类型名允许与其它变量、函数同名；</li>



<li>在类的内部，与类或类的成员同名的全局变量名和函数名将不可见。</li>
</ol>



<h2 class="wp-block-heading">构造函数</h2>



<p>定义构造函数的一般形式为：</p>



<p></p>



<pre class="wp-block-code"><code>class 类名{
    public:
    类名（形参表）;
}
类名：：类名（形参表）{//构造函数不允许返回值
    函数体;
}

}</code></pre>



<p>构造函数的名字与类名是完全相同的，并且不会返回任何类型，也不会返回void。构造函数可用于为某些成员变量设置初始值。构造函数必须是公有的。</p>



<p>构造函数可以带默认形参，也可以重载。</p>



<p>每一个类都会存在构造函数，如果用户没有写，编译器会自己创造一个；如果写了就不会重新创造了。</p>



<p>实例：构造函数：</p>



<pre class="wp-block-code"><code>class Clock{
    public:
        Clock(int H, int M, int S);//构造函数
        void setTime(int NHour, int NMinute, int NSecond);
        void ShowTime();
    private:
        int Hour;
        int Minute;
        int Second;
};
Clock::Clock(int H, int M, int S){
Hour = H;
Minute = M;
Second = S;
}

//主程序中：
Clock myClock(12, 0, 1);
myClock.showTime();//正确，实例化中调用构造函数的形参

Clock b;//错误</code></pre>



<p>构造函数的重载：</p>



<pre class="wp-block-code"><code>class Tdate{
    public:
        Tdate();
        Tdate(int d);
        Tdate(int m, int d);
        Tdate(int y, int m, int d);
    private:
        int month;
        int day;
        int year;
}</code></pre>



<p>上述示例可以在传入参数个数不同的情况下重载不同的函数。<br></p>



<h2 class="wp-block-heading">复制构造函数</h2>



<p>复制构造函数的形参是类的对象的引用</p>



<p>    Clock(const Clock &a){......}</p>



<p>复制构造函数与原来的构造函数实现了构造函数的重载</p>



<p>自动生成：如果在类定义的时候没有定义复制构造函数，编译器会生成默认的构造函数</p>



<p>何时会执行复制构造函数：</p>



<ol class="wp-block-list">
<li>通过“=”复制对象时</li>



<li>对象作为函数的参数，函数调用时</li>



<li>函数返回对象时</li>
</ol>



<p>复制构造函数是否可有可无？</p>



<p>-有些情况下可以不写，此时称为浅拷贝；</p>



<p>-有些情况必须写，此时称为深拷贝。</p>



<p>类中如果含有指针成员，执行默认复制构造函数时（浅拷贝）：</p>



<p>-会将一个原对象的指针成员复制到新对象的指针成员；</p>



<p>-若指针成员指向堆内存，在原对象生存期结束时，析构函数将堆内存释放，则新对象的指针成员会变为悬挂指针，新对象生存期结束时析构函数会出错。</p>



<p>通常来说，复制构造函数的参数是引用名。</p>



<h2 class="wp-block-heading">析构函数</h2>



<p>析构函数用于在对象生存期结束后释放掉对象所占的内存空间。</p>



<p>访问属性：公有成员函数</p>



<p>参数名：在类名前加“~”构成</p>



<p>无参数、无返回值、不能重载（因为没有参数）</p>



<p></p>



<h2 class="wp-block-heading">对象也可以作为返回对象和传递参数</h2>



<pre class="wp-block-code"><code>student f(student s){
    函数体
}</code></pre>



<p>传递的方法是值传递。</p>



<p>下面为一个例子：</p>



<pre class="wp-block-code"><code>class Tdate{
    public:
        void Tdate(int,int,int); //构造函数，实参传递给形参自动调用复制构造函数（可带默认值）
        void Print();
    private:
        int month;
        int day;
        int year;
};
void someFunction(Tdate someday){
    someday.print();
}
int main(){
    Tdate s(2003, 15, 2);
    someFunction(s);
    ........
}</code></pre>



<p>return返回对象时自动执行复制构造函数。</p>



<p>类类型的指针及引用可以作为函数的参数及返回值。由于传递的是指针，不会执行复制构造函数。</p>



<pre class="wp-block-code"><code>Tdate &someFunc(Tdate &someday){ //这里的&表示引用运算符。
    return someday;
}
int main(){
    Tdate s(2023,15,2);
    Tdate t = someFunc(s);
}</code></pre>



<pre class="wp-block-code"><code>
Tdate *someFunc(Tdate *someday){
    return someday;
}
int main(){
    Tdate s(2003, 15, 2);
    Tdate t = *someFunc(&s);//对于指针变量，传入时要取地址；*表示取地址的值。赋值给t的时候会调用复制构造函数。
}</code></pre>



<h2 class="wp-block-heading">对象指针和堆对象</h2>



<p>使用对象指针访问对象成员，使用->运算符。语法形式为：对象指针名->公有成员。</p>



<pre class="wp-block-code"><code>class Clock{
public:
    Clock(int H, int M, int S);
    void setTime(int H, int M, int S);
    void showTime();
private:
    int Hour;
    int Min;
    int Sec;
}
int main(){
    Clock* pmyclock = new Clock;//在堆区分配一个Clock的存储空间
    pmyclock->setTime(12,5,0);
    pmyclock->showTime();
    delete pmyclock;
}</code></pre>



<h2 class="wp-block-heading">this指针</h2>



<p>在类的外部访问类的成员必须要通过对象来调用。</p>



<p>在成员函数内部，访问数据成员或者成员函数时并没有看到这些成员属于哪个对象。</p>



<p>在 C++ 中，<strong>this</strong>&nbsp;指针是一个特殊的指针，它指向当前对象的实例。</p>



<p>在 C++ 中，每一个对象都能通过&nbsp;<strong>this</strong>&nbsp;指针来访问自己的地址。</p>



<p><strong>this</strong>是一个隐藏的指针，可以在类的成员函数中使用，它可以用来指向调用对象。</p>



<p>当一个对象的成员函数被调用时，编译器会隐式地传递该对象的地址作为 this 指针。</p>



<p>友元函数没有&nbsp;<strong>this</strong>&nbsp;指针，因为友元不是类的成员，只有成员函数才有&nbsp;<strong>this</strong>&nbsp;指针。</p>



<pre class="wp-block-code"><code>class Clock{
public:
    Clock(int H, int M, int S);
    void setTime(int H, int M, int S);
    void showTime();
private:
    int Hour;
    int Min;
    int Sec;
}
void Clock::setTime(int H, int M, int S){
    this->Hour = H;
    this->Min = M;
    this->Sec = S;
}
//在调用参数时，我们实际上省去了一个this指针</code></pre>



<h2 class="wp-block-heading">内部类和命名空间</h2>



<p>把一个类写在另一个类的内部，就称之为内部类。</p>



<p>内部类和普通类在使用上几乎无区别。</p>



<p>外部类不能自由访问内部类，内部类不能访问外部类。</p>



<p>内部类主要是为了避免类名的冲突。</p>



<p></p>



<pre class="wp-block-code"><code>class AAA{
    public:
        class inner{
    
    };

};</code></pre>



<p>命名空间是解决名字冲突的终极方案。只要类名在一个命名空间中唯一就好了。</p><p>The post <a href="https://blog.nanjihuaji.top/2023/12/01/c%e4%b8%ad%e7%9a%84%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/">C++中的面向对象</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://blog.nanjihuaji.top/2023/12/01/c%e4%b8%ad%e7%9a%84%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>指针（1）</title>
		<link>https://blog.nanjihuaji.top/2023/11/17/%e6%8c%87%e9%92%88%ef%bc%881%ef%bc%89/</link>
					<comments>https://blog.nanjihuaji.top/2023/11/17/%e6%8c%87%e9%92%88%ef%bc%881%ef%bc%89/#comments</comments>
		
		<dc:creator><![CDATA[Nanjihuaji]]></dc:creator>
		<pubDate>Fri, 17 Nov 2023 07:56:43 +0000</pubDate>
				<category><![CDATA[C++笔记]]></category>
		<category><![CDATA[编程]]></category>
		<guid isPermaLink="false">http://temp.nanjihuaji.top/?p=86</guid>

					<description><![CDATA[<p>指针是什么 指针是一类存储一个地址的变量。声明方式为* . 所有的指针变量大小都是8个字节（64位系统）或者4 [&#8230;]</p>
<p>The post <a href="https://blog.nanjihuaji.top/2023/11/17/%e6%8c%87%e9%92%88%ef%bc%881%ef%bc%89/">指针（1）</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading">指针是什么</h1>



<p>指针是一类存储一个地址的变量。声明方式为<数据类型>* <变量名>.</p>



<p>所有的指针变量大小都是8个字节（64位系统）或者4个字节（32位系统），因为其中存储的是一个<strong>地址</strong>（16进制整数），这与指向的类型是无关的。但是，指向的变量类型必须与指针前面的<数据类型>一致。例如：</p>



<pre class="wp-block-code"><code>int A = 1;
int *pA = &A;</code></pre>



<p>就是一个正确且典型的声明指向整型变量A的实例，但下面这个示例就是错的：</p>



<pre class="wp-block-code"><code>int A = 1;
char *pA = &A;</code></pre>



<p>这是为什么呢？想一想，int类型的变量占4个字节，而char类型的变量占1个字节.  请注意<strong>一个内存单元是1字节的</strong>，这表明对于int A的引用需要引用4个字节，而对char类型的引用只需要1个字节. <s>（应该是这样的吧）</s></p>



<p>使用*<变量名>可以对指针所指向的变量进行引用.</p>



<p>int* const <变量名>是一个指针常量，也就是指针<strong>本身</strong>是一个<strong>常量</strong>，指针所指向的地址不能修改，但指针所指向的内容可以修改.</p>



<p>const int* <变量名>是一个常量指针，也就是指针<strong>所指向的</strong>是一个<strong>常量</strong>，指针所指向的地址可以修改，但指针所指向的内容不可修改.</p>



<p>const int* const <变量名>是一个指向常量的常量指针，指针所指向的地址和内容都不可以修改.</p>



<figure class="wp-block-table"><table><tbody><tr><td>int* const A</td><td>const int* A</td><td>const int* const A</td></tr><tr><td>A是常量</td><td>A是变量</td><td>A是常量</td></tr><tr><td>指针所指地址是变量</td><td>指针所指地址是常量</td><td>指针所指地址是常量</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">那么，指针到底是什么呢？</h2>



<p>例如，我们之前已经声明了一个整型变量A，A=1且存储在内存0x100。我们声明int* pA = &A后，pA是一个指针变量，其中存储的数据是0x100，也就是A的地址.</p>



<p>我们可以通过*pA对A进行<strong>间接引用 </strong>.</p>



<p>下面举个例子，假设现在要写一个swap函数，交换输入的两个参数，应该怎么办呢？</p>



<p>下面是一个似乎没有错误的写法：</p>



<pre class="wp-block-code"><code>void swap(int x, int y){
    int t = x;
    x = y;
    y = t;
} </code></pre>



<p>尝试后是不能交换x和y的（为什么呢？），理由如下：</p>



<p>step 1. 将输入的参数（定为a和b）赋值给函数内的形参x和y；</p>



<p>step 2. 将函数内的形参x, y交换 ；</p>



<p>step 3. 由于没有返回值，函数在这里运行完毕，x和y被释放掉.</p>



<p>那么应该怎么写呢？</p>



<pre class="wp-block-code"><code> void (int *x, int *y){
    int t;
    t = *x;
    *x = *y;
    *y = t;
}</code></pre>



<p>这种写法是可行的，下面说明理由：</p>



<p>step 1. 传入参数&a和&b，将其赋值给x和y两个指针变量;</p>



<p>step 2. 通过指针变量x和y简介改变&a和&b中存储的值</p>



<p>step 3. 由于没有返回值，函数运行结束，x和y被释放掉.</p>



<p>我们注意到，由于这个写法是直接改变的a和b地址里对应的值，所以这种写法是切实可行的.</p>



<h2 class="wp-block-heading">指针函数是和函数指针是什么</h2>



<p>指针函数是一个返回值为指针的<strong>函数</strong>，声明方式为<数据类型>* 变量名(形式参数).</p>



<p>需要注意的是，<em>指针函数不能返回一个过程中的地址，因为这些地址所指向的内存会被释放掉</em>.</p>



<p>函数指针是一个指针<strong>变量</strong>，指向一个函数. 声明方式为<数据类型> (*函数名)(参数).</p>



<p>函数指针需要赋一个函数的地址值给它，赋值方式为：</p>



<pre class="wp-block-code"><code>int (*fun)(int x, int y);
fun = &Function; </code></pre>



<p>或者：</p>



<pre class="wp-block-code"><code>int (*fun)(int x, int y);
fun = Function;</code></pre>



<p>取地址符<strong>不是</strong>必须的，因为函数名本身就代表一个地址.</p>



<p>函数指针可以被这样调用：</p>



<pre class="wp-block-code"><code>u = f();
//或者
v = (*f)();</code></pre>



<p>这两种方法是等价的. 第一种方法看起来和普通的函数调用没有什么区别.</p>



<p>实例：</p>



<pre class="wp-block-code"><code>int add(int x,int y){
    return x+y;
}
int sub(int x,int y){
    return x-y;
}
//函数指针
int (*fun)(int x,int y);

int main(int argc, char *argv&#91;])
{
    QApplication a(argc, argv);
    //第一种写法
    fun = add;
    qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ;
	//第二种写法
    fun = &sub;
    qDebug() << "(*fun)(5,3) = " << (*fun)(5,3)  << fun(5,3)；

    return a.exec();
}

</code></pre>



<h2 class="wp-block-heading">指针与字符串</h2>



<figure class="wp-block-table"><table><tbody><tr><td>已定义</td><td>char s_array[]=”This is a book”</td><td>是否可以</td><td>Char* s_pt = “This is a book”;</td><td>是否可以</td></tr><tr><td>直接输出</td><td>cout << s_array;</td><td>可以</td><td>cout << s_pt;</td><td>可以</td></tr><tr><td>直接输入</td><td>cin >> s_array;</td><td>可以</td><td>cin >> s_pt;</td><td>不可以</td></tr><tr><td>直接更改</td><td>s_array = “OK”;</td><td>不可以</td><td>s_pt = “OK”;</td><td>可以</td></tr><tr><td>赋值</td><td>s_array = s_pt;</td><td>不可以</td><td>s_pt = s_array;</td><td>可以</td></tr></tbody></table></figure>



<p>通过cin来修改指针所指的内容是不允许的，但是指针本身是可以被修改的。</p>



<p>string.h中提供了这些函数：</p>



<figure class="wp-block-table"><table><tbody><tr><td>功能</td><td>函数形式</td><td>返回值</td><td>说明</td></tr><tr><td>字符串长度</td><td>int strlen(const char *string);</td><td>字符串长度.</td><td>‘\0’不计入.</td></tr><tr><td>字符串复制</td><td>char *strcpy(char *dest, const char *src);</td><td>把 src 所指向的字符串复制到 dest，替换对应的字符（scr中的’\0’也会参与替换）.<br>该函数返回一个指向最终的目标字符串 dest 的指针.</td><td>注意缓冲区溢出问题.</td></tr><tr><td>按字符数复制</td><td>char *strncpy(char *dest, const char *src, size_t n);</td><td>把 src 所指向的字符串复制到 dest，最多复制 n 个字符.<br>该函数返回一个指向最终的目标字符串 dest 的指针.</td><td>当 src 的长度小于 n 时，dest 的剩余部分将用空字节填充.</td></tr><tr><td>字符串按字符数比较</td><td>int strncmp(const char *str1, const char *str2, size_t n);</td><td><strong>strncmp()</strong>&nbsp;函数通常用于比较两个字符串，以确定它们是否相等或哪个字符串在字典顺序上更小.<br>返回一个整数.</td><td>如果返回值 < 0，则表示 str1 小于 str2。<br>如果返回值 > 0，则表示 str1 大于 str2。<br>如果返回值 = 0，则表示 str1 等于 str2。</td></tr><tr><td>字符串比较</td><td>int strcmp(const char *str1, const char *str2);</td><td>把 str1 所指向的字符串和 str2 所指向的字符串进行比较.</td><td>如果返回值 < 0，则表示 str1 小于 str2。<br>如果返回值 > 0，则表示 str1 大于 str2。<br>如果返回值 = 0，则表示 str1 等于 str2。</td></tr><tr><td>字符串连接</td><td>char *strcat(char *dest, const char *src);</td><td>把&nbsp;<strong>src</strong>&nbsp;所指向的字符串追加到&nbsp;<strong>dest</strong>&nbsp;所指向的字符串的结尾.<br>该函数返回一个指向最终的目标字符串 dest 的指针.</td><td>dest数组需要有足够的空间用以容纳src.<br>src中的内容不会覆盖dest中的内容.</td></tr></tbody></table></figure>



<p>对于const char*，可以使用”string”作为参数；对于char *，只能用一个数组名（地址）作为参数.</p>



<h2 class="wp-block-heading">指针和数组</h2>



<p>数组名本身就是地址，对于一维数组，它指向第一个（索引为0）元素.</p>



<p>指针和数组名联系起来，可以通过指针访问数组.</p>



<p>对于二维数组（假设为p[3][4]），它指向第一行第一列.</p>



<h2 class="wp-block-heading">一维数组</h2>



<p>一维数组名可以看作指针，例如int A[100];，数组名A和&A[0]都是访问第一个元素的地址.</p>



<p>通过*A就可以间接访问数组A的第一个（索引为0）元素，若要访问数组A中的第i-1个（索引为i）元素，可以采用：</p>



<p>*(A+i)，这等价于A[i].</p>



<p>例如：</p>



<pre class="wp-block-code"><code>int A&#91;10], *pa = A;//等价于: int &#91;A]; int* pa; pa = A; 
//注意后面的那句话中pa = A，pa前不加*是因为pa和A都是存储地址的.
//那么我们可以继续注意到：
*(pa+1);
*(A+1)
A&#91;1];
*pa++; //等价于*(pa++)，++是右结合运算符，比*优先.
//都是等价的</code></pre>



<h2 class="wp-block-heading">指针数组</h2>



<p>指针数组是一个数组，其元素是指针.</p>



<p>声明：<数据类型>* <数组名>[常数];</p>



<p>例如：char *membet_name[] = {“Merry”, “John”, “Hill”};</p>



<p>数组元素指向一些不同长度的字符串。下面举个例子：</p>



<pre class="wp-block-code"><code>char *membet_name&#91;] = {"Merry", "John", "Hill"};
for(int i = 0; i <= 2; i++){
    cout << member_name&#91;i] << endl;
}
</code></pre>



<p>输出为：</p>



<pre class="wp-block-code"><code>Merry
John
Hill</code></pre>



<p>指针数组可以作为形式参数（例如传入main函数，下面是一个示例）</p>



<pre class="wp-block-code"><code>int main(int argc, char* argv&#91;]){
    cout << "共输入了" << argc << "个参数," << "分别是\n";
    for(int i = 0; i < argc; ++i){
        cout << argv&#91;i]<< ' ';
    }
}

</code></pre>



<h2 class="wp-block-heading">二维数组</h2>



<p>二维数组名可以看作指针，它具有以下特点：</p>



<p>二维数组可以看成是一维数组的一维数组</p>



<p>对二维数组B[3][4]，数组名B的地址，是其中第一个一维数组B[0]的地址（这恰好也是B[0][0]的地址，所以B指向的元素恰好就是B[0][0]!)。</p>



<p>访问第i行j列的数组元素（i，j指的是索引）：</p>



<p>*(*(<数组名>+i)+j)</p>



<figure class="wp-block-table"><table><tbody><tr><td>B[0][0]默认访问的元素，等于*B，也是二维数组中的第一个一维数组</td><td>*(*B+1)</td><td>*(*B+2)</td></tr><tr><td>*(B+1)所访问的地址，也是二维数组中的第二个一维数组</td><td>*(*(B+1)+1)</td><td>*(*(B+1)+2)</td></tr><tr><td>*(B+2)所访问的地址，也是二维数组中第三个一维数组.</td><td>*(*(B+2)+1)</td><td>*(*(B+2)+2)</td></tr></tbody></table></figure>



<p>假设数组名为B.</p>



<h2 class="wp-block-heading">指针和结构体</h2>



<p>下面声明一个结构体：</p>



<pre class="wp-block-code"><code>struct student{
int id;
float score;
};
stu = {123, 123};
//指针访问结构体中的元素有点操作符"."和尖头操作符"->".
student* ps = &stu;
cout << (*ps).score << ps->score;//均是输出stu的score.
</code></pre>



<p>也可以使用new来申请分配一块空间存放</p>



<pre class="wp-block-code"><code>struct Employee{
    char name&#91;20];
    unsigned long id;
    float salary;
};
int main(){
    Employee* prPtr = new Employee;  //直接指定一块新的空间用来存放
    strcpy(prPtr->name, "Zhang San"); // 将new Employee中存放的Employee的name变为张三
    prPtr->id = 1145141919810;
    prPtr->salary = 114514.1919810;
    cout << prPtr->name << prPtr->id << prPtr->salary << endl;
    delete prPtr; //删除申请的prPtr空间

</code></pre>



<h2 class="wp-block-heading">链表</h2>



<p>例：单向链表的建立：</p>



<pre class="wp-block-code"><code>struct student{
    long num;
    char name&#91;20];
    float score;
    <strong>student* next;</strong>  //指向下一个学生的指针
};
    int main(){
        student* head = NULL, *temp = NULL;
        head = new student;//head为头结点指针
        temp = head;
        int i = 1;
    while(temp != NULL){
            temp->num = i;
            cout << "PLZ input name and score for No." << i << endl;
            cin >> temp->name >> temp->score;
            temp->next = NULL;
            i++;
    }</code></pre>



<h2 class="wp-block-heading">void类型指针</h2>



<p>void指针也指向内存地址，但是不指定这个地址单元的数据类型.</p>



<p>声明：void* <指针名>;</p>



<p>void*可以接受任意类型的指针赋值，但是void*<strong>不能直接</strong>复制给其它指针，如果要将 void 指针 p 赋给其他类型的指针，则需要强制类型转换。按照ANSI标准，不能对 void 指针进行算法操作。</p><p>The post <a href="https://blog.nanjihuaji.top/2023/11/17/%e6%8c%87%e9%92%88%ef%bc%881%ef%bc%89/">指针（1）</a> first appeared on <a href="https://blog.nanjihuaji.top">南极滑稽的博客</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://blog.nanjihuaji.top/2023/11/17/%e6%8c%87%e9%92%88%ef%bc%881%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
