类和对象那些事儿(b)

本文深入探讨了C++的高级特性,包括友元函数的功能与定义,this指针的用途及使用场景,类的静态成员的特性与创建方法。同时,文章提供了丰富的代码实例,帮助读者理解并掌握这些关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题前瞻:
1.友元函数的功能和使用?独特性和便利性?
2.this 指针,
3.指向类的指针?
4.C++ 类的静态成员?

1.C++友元函数
1.1 功能
友元函数有权访问累的所有私有成员和保护成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
1,2 定义
友元可以是一个函数,也可以是一个类,成为友元类。
如果声明一个函数为类的友元函数,需要在类中提供函数原型时,加上前缀friend。
eg:

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};

声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:friend class ClassTwo;

2.C++中的this 指针
2.1 功能
C++中,每一个对象都能通过this指针来访问自己的地址。this指针是所有成员函数的隐含参数。因此,在成员函数内部,他可以用来指向调用对象。
2.2 使用
eg:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      int compare(Box box)
      {
         return this->Volume() > box.Volume();	//		this用于指向被调用的对象。 
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   if(Box1.compare(Box2))
   {
      cout << "Box2 is smaller than Box1" <<endl;
   }
   else
   {
      cout << "Box2 is equal to or larger than Box1" <<endl;
   }
   return 0;
}
结果如下:
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

this 是默认的指向自己的指针,那么一般情况下指向类的指针该怎么创建和使用呢?

  1. C++中指向类的指针
    指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。
    所以:指向类的指针和指向结构的指针用法一样。
#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   Box *ptrBox;                // Declare pointer to a class.

   // 保存第一个对象的地址
   ptrBox = &Box1;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;

   // 保存第二个对象的地址
   ptrBox = &Box2;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;
  
   return 0;
}
  1. C++类的静态成员
    4.1 为什么引入静态,静态成员有什么特点?
    当我们声明类成员是静态的时候意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为0.
    静态变量不能放置在类的定义中,但是可以在类的外部通过范围解析运算符::来重新声明静态变量进行初始化。
    4.2 创建和使用
    eg:
#include <iostream>
 
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2

   // 输出对象的总数
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Constructor called.
Constructor called.
Total objects: 2

4.3 静态函数成员
4.3.1 功能
如果把函数成员声明为静态的,就可以把 函数 与 类的任何一个特定对象独立开来。
静态成员函数即使在类对象 不存在的情况下 也能被调用,
只能访问静态的 数据成员,不能访问其他静态成员函数和类外部其它函数
静态成员函数有一个类范围,他们不能访问类的this指针,所以可以使用静态成员函数来判断类的某些对象是否已经被创建。

4.3.2 创建和使用
eg:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
  
   // 在创建对象之前输出对象的总数
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2

   // 在创建对象之后输出对象的总数
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
<think>我们正在讨论MFC中的CDC内存绘图问题。用户提到在使用CDC内存绘图时背景自动变黑,需要分析原因并提供解决方法。 根据引用[2]中关于位图DC与内存DC的说明,我们可以推断内存绘图通常涉及创建兼容的内存设备上下文(CDC)位图(CBitmap)。背景变黑的原因可能是在创建位图时没有初始化,或者选定位图后没有用背景色填充。 原因分析: 1. 创建的内存位图是未初始化的,其内容通常是随机的(在调试模式下可能被填充为黑色)。 2. 在将位图选入内存DC后,没有使用背景色(例如白色)填充整个位图,导致显示时出现黑色背景。 解决方法: 1. 在创建位图后,将其选入内存DC。 2. 使用背景色(例如白色)填充整个位图区域。 示例代码步骤: a. 创建兼容的内存DC:`CDC memDC; memDC.CreateCompatibleDC(pDC);` b. 创建兼容的位图:`CBitmap memBitmap; memBitmap.CreateCompatibleBitmap(pDC, width, height);` c. 将位图选入内存DC:`CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);` d. 用背景色填充内存DC:`memDC.FillSolidRect(0, 0, width, height, RGB(255, 255, 255));` // 使用白色填充 e. 进行绘图操作 f. 将内存DC的内容绘制到目标DC(如屏幕DC) g. 恢复旧位图:`memDC.SelectObject(pOldBitmap);` 注意:在填充背景色时,可以使用所需的任何颜色。如果不填充,则内存位图的内容是未定义的,通常显示为黑色。 另外,引用[1]中提到设置背景透明的方法,但这里我们是在绘制之前需要先设置背景色,而不是设置透明。因为内存DC的位图一开始是空的,需要先填充背景。 如果用户希望背景透明,则可能需要使用透明位图(32位位图)或者使用掩码技术(先绘制背景,再绘制前景),但用户的问题主要是背景变黑,所以填充背景色即可。 根据引用[3]中关于绘制文本时调整矩形区域的例子,我们可以知道在内存DC上绘制文本或图形时,同样需要注意背景的填充。 因此,核心解决方法是:在创建内存位图后立即用背景色填充。 下面提供一个示例代码片段: ```cpp void CMyView::OnDraw(CDC* pDC) { // 假设我们要绘制一个宽度为100,高度为100的图形 int width = 100; int height = 100; CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap memBitmap; memBitmap.CreateCompatibleBitmap(pDC, width, height); CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap); // 填充白色背景(或其他颜色) memDC.FillSolidRect(0, 0, width, height, RGB(255, 255, 255)); // 接下来在memDC上进行绘图操作,例如画一个红色的矩形 memDC.SelectStockObject(BLACK_PEN); memDC.SelectStockObject(NULL_BRUSH); memDC.Rectangle(10, 10, 90, 90); // 将内存DC的内容绘制到屏幕DC pDC->BitBlt(0, 0, width, height, &memDC, 0, 0, SRCCOPY); // 恢复旧位图并删除资源 memDC.SelectObject(pOldBitmap); memBitmap.DeleteObject(); memDC.DeleteDC(); } ``` 这样,背景就不会是黑色,而是我们填充的白色。 如果用户仍然遇到问题,可能还需要检查是否在绘制过程中覆盖了整个区域,或者是否有其他绘制操作改变了背景。 另外,如果用户希望背景透明(即显示底层窗口的背景),则不能简单地用颜色填充,而是需要将内存位图创建为32位位图(支持alpha通道),或者使用掩码技术(先绘制背景,再绘制前景)。但根据用户当前的问题描述,背景变黑只需要填充背景色即可解决。 因此,总结解决方法:在内存DC上绘制之前,使用FillSolidRect填充背景色。 §§相关问题§§ 1. 如何创建支持透明背景的内存DC? 2. 除了FillSolidRect,还有哪些方法可以设置内存DC的背景? 3. 内存绘图时如何提高性能?</think>### CDC内存绘图背景变黑的原因及解决方法 #### 原因分析 1. **位图未初始化** 创建兼容位图时,新位图内容未初始化(默认填充黑色),导致背景变黑: $$ \text{CreateCompatibleBitmap}() \rightarrow \text{未初始化的黑色位图} $$ 2. **缺少背景填充** 内存DC在绘制前未清除背景,直接显示原始黑色像素[^1]。 3. **透明设置未生效** 透明背景设置(如`SetBkMode(TRANSPARENT)`)仅影响文本绘制,对位图本身无效[^1]。 #### 解决方法 ```cpp // 1. 创建内存DC兼容位图 CDC memDC; memDC.CreateCompatibleDC(pDC); // pDC为目标设备上下文 CBitmap memBitmap; memBitmap.CreateCompatibleBitmap(pDC, width, height); // 创建空白位图 // 2. 填充背景色(关键步骤) CBitmap* pOldBmp = memDC.SelectObject(&memBitmap); memDC.FillSolidRect(0, 0, width, height, RGB(255,255,255)); // 填充白色背景 // 3. 执行绘图操作(此时背景已为白色) memDC.MoveTo(0,0); memDC.LineTo(100,100); // 4. 输出到目标DC(如屏幕) pDC->BitBlt(0, 0, width, height, &memDC, 0, 0, SRCCOPY); // 5. 清理资源 memDC.SelectObject(pOldBmp); memBitmap.DeleteObject(); ``` #### 透明背景处理技巧 若需透明背景,需使用32位位图并设置Alpha通道: ```cpp // 创建32位位图(支持Alpha通道) BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; // 顶向下 bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; // 关键:32位色深 bmi.bmiHeader.biCompression = BI_RGB; void* pBits = nullptr; HBITMAP hAlphaBmp = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &pBits, NULL, 0); // 初始化透明背景(RGBA: A=0表示全透明) DWORD* pPixels = (DWORD*)pBits; for(int i=0; i<width*height; ++i) pPixels[i] = 0x00000000; // Alpha=0 ``` #### 关键要点 1. **必须显式填充背景** 使用`FillSolidRect()`或`PatBlt()`初始化背景色。 2. **透明背景需特殊处理** 标准位图不支持透明,需使用32位位图+Alpha混合[^2]。 3. **资源管理** 务必恢复旧位图并删除对象,避免内存泄漏。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值